Layered Architecture
A responsibility-first layout for larger APIs with clear boundaries between routes, controllers, services, and repositories.
Layered architecture organizes code by responsibility.
Instead of placing all users files in one folder, each type of file has a top-level folder. Routes go in src/routes, controllers go in src/controllers, services go in src/services, and repositories go in src/repositories.
When To Use Layered
Use layered when:
- The API has many features
- The team wants strict boundaries between responsibilities
- Business logic should be easy to test outside HTTP
- Data access should be isolated behind repositories
- You want a structure that scales as the project grows
Structure
src/
server.ts
routes/
health.route.ts
users.route.ts
controllers/
health.controller.ts
users.controller.ts
services/
health.service.ts
users.service.ts
repositories/
health.repository.ts
users.repository.ts
interfaces/
health.interface.ts
users.interface.ts
validators/
health.validation.ts
users.validation.ts
shared/
config/Request Flow
Request
|
v
route
|
v
controller
|
v
service
|
v
repository
|
v
ResponseLayer Responsibilities
| Layer | Responsibility | Should avoid |
|---|---|---|
| Route | HTTP path registration | Business rules |
| Controller | Request and response handling | Data persistence details |
| Service | Business rules and orchestration | Framework-specific request objects |
| Repository | Data access boundary | HTTP response formatting |
| Interface | TypeScript contracts | Runtime logic |
| Validator | Validation schemas or helpers | Business orchestration |
Add-Module Depth
Layered projects still let you choose how much of the stack to generate:
| Choice | Files |
|---|---|
| Route + Controller | routes, controllers |
| Route + Controller + Service | routes, controllers, services |
| Route + Controller + Service + Repository | routes, controllers, services, repositories |
| Full | All layers, including interfaces and validators |
Why Use Layered
Layered projects make dependencies visible:
controller depends on service
service depends on repository
repository does not depend on controllerThat direction matters. It keeps business logic away from framework-specific HTTP details and makes service tests easier to write later.
Trade-Off
Layered creates more files. For a tiny API, that can feel heavy. For a growing production API, the extra structure usually pays for itself through clearer ownership and easier testing.