Clean Architecture¶
dichit-backend is organised around Clean Architecture: source-code dependencies point inward, toward the domain, and never outward. This is the single most important constraint in the codebase — it is enforced by review, not by a compiler rule (see coding-standards).
The Dependency Rule¶
flowchart TB
subgraph Outer[Outer layers - frameworks & adapters]
IF["interfaces/<br/>Fastify, routes, WS, webhooks"]
INF["infrastructure/<br/>Prisma, Redis, AWS, queue, auth"]
end
subgraph Mid[Application - use cases & contracts]
APP["application/<br/>use-cases, services, queue processors"]
IF2["application/interfaces/<br/>dependency contracts (repositories, services)"]
end
subgraph Inner[Domain]
DOM["domain/<br/>entities + helpers"]
end
IF --> APP
INF --> IF2
APP --> DOM
IF2 -. "implemented by" .-> INF
style Inner fill:#e8f5e9 Dependency direction is the inverse of execution flow: controllers call use cases, use cases call interfaces (src/application/interfaces/), and the concrete implementations in src/infrastructure/ satisfy those interfaces at runtime via Awilix (see dependency-flow.md and dependency-injection).
Layer responsibilities¶
| Layer | Owns | Never does |
|---|---|---|
domain/ | Entities, enums, value logic | No IO, no framework imports |
application/ | Orchestration, business rules, contracts | Talks directly to Prisma/AWS/Redis |
infrastructure/ | Repositories, providers, config, auth, queue | Contains business rules |
interfaces/ | Routing, validation wiring, controllers, WS | Contains business logic |
shared/ | Cross-cutting constants, errors, zod/swagger schemas | — |
Rules of thumb (from AGENTS.md)¶
- Never use
any. - Never put business logic inside controllers.
- Never call Prisma directly inside controllers.
- Never introduce new libraries unless explicitly requested.
- Never refactor unrelated files.
- If unsure where code belongs, prefer
application/.
Dependency flow in practice¶
flowchart LR
C["Controller (interfaces)"] --> UC["Use case (application)"]
UC --> RepoI["Repository interface (application/interfaces)"]
UC --> SvcI["Service interface (application/interfaces)"]
RepoI --> Repo["PrismaRepository (infrastructure)"]
Repo --> PG[("PostgreSQL")]
SvcI --> Svc["ProviderService (infrastructure)"]
Svc --> EXT["S3 / Redis / FCM / SMS"] Consequences¶
Good: testable use cases, swappable providers, minimal framework leakage, predictable feature layering.
Costs: more files per feature, interface ceremony, and the discipline of reviewing imports. The payoff is that features like a repository or a payment adapter can change without touching callers.