A single feature module from a production Express + Drizzle + Zod monorepo. Shows the patterns without the full template.
src/modules/credits/
index.ts # Public API (gatekeeper)
router.ts # Express route wiring
api/
balance.controller.ts # Thin HTTP adapter
core/
credit.schema.ts # Zod validation
credit.service.ts # Pure business logic (DB ops)
listeners/
payment-success.listener.ts # Event bus side-effect
- Group by Feature — everything
creditsneeds lives insidesrc/modules/credits/. - Public API —
index.tsexports only what other modules may use. Internals stay private. - Zero Horizontal Coupling — modules never import each other's internals. Cross-module communication happens via a typed event bus.
- Thin Controllers — controllers parse the request, call a service, return a typed response. No business logic.
- Pure Services — all domain logic lives in
core/. Pure functions, no classes, Zod for validation. - Event-Driven Side Effects — when a payment succeeds, the payments module emits
PAYMENT_SUCCEEDED. The credits listener reacts. Neither module knows the other exists.
kebab-casefor all filenamescamelCasefor functions/variablesUPPER_SNAKE_CASEfor constants- No classes — pure functions only
- Zod schemas colocated with the feature, not in a global
schemas/folder