Clean Architecture
Implement clean architecture - use cases, entities, SOLID principles, dependency inversion, and organizing code for testability and maintainability.
skill: Clean Architecture category: Backend tags: [Clean Architecture, Uncle Bob, Use Cases, Dependency Inversion, SOLID] readingTime: 6
Clean Architecture's core insight: the dependency arrow always points inward - outer layers (database, web framework) depend on inner layers (domain, use cases), never the reverse. This means you can swap your web framework or database without touching business logic. The cost is indirection; the benefit is testability and flexibility.
Workflow
- Structure layers correctlyClean Architecture has four concentric layers: Entities (innermost), Use Cases, Interface Adapters, and Frameworks/Drivers (outermost). Dependencies flow inward only.
- Write use cases as single-responsibility orchestratorsUse cases contain application-specific business rules - they orchestrate domain entities and call ports. One use case per business operation.
- Apply SOLID principlesSOLID principles prevent common OOP design mistakes that lead to tightly coupled, hard-to-test code.
- Wire dependencies with a containerDependency injection containers wire the layers together at application startup - each layer gets its dependencies injected, enabling swap for testing.
- Test each layer independentlyThe dependency inversion in clean architecture enables fast, isolated tests - domain logic tested with no database, use cases tested with mocks, adapters tested against real infrastructure.
Output Format
Clean architecture: four-layer structure, use case as orchestrator with port interfaces, SOLID principles, manual DI for wiring, and three test levels (domain/use-case/integration).
domain/entities → application/ports (interfaces) + use-cases
adapters/controllers (HTTP→useCase) + repositories (implements port)
infrastructure/server + database
DI: inject real adapters in production, mocks in tests
Tests: domain (pure) + use-case (mocks) + integration (real DB)Examples
Edge Cases & Gotchas
- Over-abstracted simple projects - Clean Architecture adds 3x more files than a simple Express + Prisma app. For a simple CRUD API, the indirection isn't worth it. Scale the architecture to the complexity of the domain.
- Shared kernel between layers - DTOs (data transfer objects) that cross layer boundaries are often shared - avoid importing domain entities into the HTTP response layer. Use separate response DTOs mapped in the controller.
- Use case granularity - One use case per business operation means dozens of use case classes. For simple operations (get by ID), a shared QueryService is acceptable - reserve full use case classes for complex operations with business rules.
- Circular dependencies in DI - Service A depends on B which depends on A → circular injection error. Resolve by extracting the shared concern into a third service or using an event to break the cycle.
When to Break These Rules
- Use a simple service layer (controller → service → repository) for CRUD applications - clean architecture's benefit is proportional to domain complexity, not code volume.
- Use a framework-centric structure (Next.js app/, NestJS modules) when the framework enforces its own architecture - fighting the framework's structure adds friction without benefit.
Reference Files
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.htmlhttps://khalilstemmler.com/articles/software-design-architecture/solid-principles/