Struktur folder untuk proyek kecil-sedang: modular schema per domain, centralized relations, query service pattern untuk isolasi queries dari routes, serta scaling ke microservices dengan shared schema package dan database per service.

Setelah di episode 18 kita mempelajari Testing dengan In-Memory SQLite — unit testing dan integration testing — pada episode ini kita membahas project structure: bagaimana mengorganisasi file dan folder dalam proyek Drizzle ORM agar scalable, maintainable, dan mudah dipahami oleh tim.
Project structure yang baik mengurangi technical debt dan memudahkan onboarding developer baru.
src/
db/
schema/ # Drizzle schema definitions
users.ts
posts.ts
relations.ts # Centralized relations
index.ts # db instance export
migrations/ # Generated by drizzle-kit
services/ # Business logic, queries
user.service.ts
post.service.ts
routes/ # API routes
user.routes.ts
post.routes.ts
drizzle.config.tsdb/schema/: satu file per tabel/domain. Memudahkan navigasi.db/index.ts: export db instance. Import dari satu tempat.services/: isolasi queries dari routes. Reusable query functions.routes/: API routes. Memanggil service, bukan query langsung.import { pgTable, serial, text, integer } from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
})import { pgTable, serial, text, integer } from 'drizzle-orm/pg-core'
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
authorId: integer('author_id').references(() => users.id),
})import { relations } from 'drizzle-orm'
import { users } from './users'
import { posts } from './posts'
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}))
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}))import { db } from '../db'
import { users } from '../db/schema'
import { eq, and } from 'drizzle-orm'
export async function getAllUsers() {
return db.select().from(users)
}
export async function getUserById(id: number) {
return db.select().from(users).where(eq(users.id, id))
}
export async function createUser(data: InsertUser) {
const [user] = await db.insert(users).values(data).returning()
return user
}import { getAllUsers, getUserById, createUser } from '../services/user.service'
app.get('/users', async () => {
const users = await getAllUsers()
return users
})
app.get('/users/:id', async ({ params }) => {
const user = await getUserById(Number(params.id))
return user
})Tip
Query service pattern memungkinkan kalian mengganti implementation tanpa mengubah routes. Jika perlu migrate dari Drizzle ke ORM lain, cukup ubari service layer — routes tidak terpengaruh.
Untuk microservices yang berbagi database, buat schema sebagai npm package terpisah:
packages/
shared-schema/ # npm package
src/
users.ts
posts.ts
package.json
api-service/ # Microservice 1
src/
routes/
package.json
web-service/ # Microservice 2
src/
routes/
package.jsonMicroservices pattern merekomendasikan database per service — setiap service punya database sendiri. Schema pattern yang sama bisa digunakan di setiap service.
Pada episode 19 ini, kalian telah memahami Project Structure & Scalability:
db/schema/, services/, routes/.Di episode 20 selanjutnya — episode terakhir — kita akan mempelajari Production Deployment & Best Practice — database connection management, migration strategy, performance monitoring, security, CI/CD integration, dan health check endpoint. Sampai jumpa di episode 20!