principletypescriptMajor
Clean Architecture Layers: Dependency Rule Points Inward
Viewed 0 times
clean architecturedependency ruleonion architecturehexagonal architectureports and adaptersdomain isolationcomposition root
Problem
Business logic imports database drivers, HTTP libraries, and UI framework code. Swapping a framework or database requires touching core business rules. Testing business logic requires running real infrastructure.
Solution
Organize code into concentric layers: Entities (innermost) > Use Cases > Interface Adapters > Frameworks & Drivers (outermost). The Dependency Rule: source code dependencies only point inward — outer layers may depend on inner layers, never the reverse.
src/
domain/ # Innermost: entities, value objects, domain services
entities/
order.ts # No imports from outside domain/
ports/ # Interfaces owned by domain
order.repository.ts
application/ # Use cases — depends on domain only
use-cases/
place-order.ts # imports from domain, never from infrastructure
infrastructure/ # Implements domain ports
repositories/
prisma-order.repository.ts # implements domain/ports/order.repository
http/
order.controller.ts # imports from application use cases
main.ts # Composition root — wires everything together// domain/use-cases/place-order.ts — NO framework imports
import type { OrderRepository } from '../ports/order.repository';
export class PlaceOrderUseCase {
constructor(private repo: OrderRepository) {}
async execute(dto: PlaceOrderDto) { /* pure business logic */ }
}Why
The dependency rule ensures the domain is independently deployable and testable. The application core has no knowledge of databases, HTTP, or UI frameworks, so it can be tested with in-memory stubs at full speed.
Gotchas
- The boundary between Use Cases and Interface Adapters is often the hardest to maintain — DTOs should cross boundaries and be translated at the adapter layer, not domain entities.
- Do not put framework annotations (e.g., TypeORM
@Entity, NestJS@Injectable) on domain entities — that couples the domain to the framework. - The composition root (main.ts or bootstrap.ts) is the one place allowed to know all layers — it wires interfaces to implementations.
Revisions (0)
No revisions yet.