When is clean architecture over-engineering, and what is the signal that you have its costs without its benefits?
Show the full answer Hide the answer
What is being tested
Whether you can identify the specific failure mode that produces all of the ceremony and none of the protection.
The signal: an anaemic domain with full ceremony
You have the mapping between domain, persistence and API types. You have the interfaces, the directory structure, the dependency rule enforced. And your entities are data bags with getters and setters while all the logic sits in service classes.
If the domain objects have no behaviour, clean architecture is protecting nothing. You are paying the full cost — three types per concept, mapping code between them, extra indirection, a steeper onboarding curve — to isolate a domain that consists of field assignments.
That is the diagnostic, and it is common because the structure is easier to copy than the modelling discipline it exists to support.
When it is over-engineering
CRUD over a database. If the "domain" is a table and the use case is "save it", the mapping ceremony exceeds the logic being protected. A straightforward layered application is better, more readable, and faster to change.
Short-lived systems. The benefit accrues over years, as frameworks change and the domain grows. A system with a two-year life does not collect it.
Small teams with one delivery mechanism and no plausible infrastructure change. The option being purchased will never be exercised.
When it is worth it
Rich domain logic with a long life — insurance policies, financial instruments, healthcare scheduling, logistics rules, tax calculation. The complexity is genuine, it is the source of competitive value, and the system will outlive several generations of frameworks.
The strongest concrete benefit in practice is testing: a full business-logic suite that runs in seconds with no infrastructure changes how often people run it, which compounds over years.
The other failures worth naming
- Framework types leaking inward — an ORM entity or an HTTP request object in the domain, which silently inverts the dependency rule while leaving the structure intact.
- Interfaces for everything, including things with one implementation and no plausible second.
- Inconsistent application across a codebase, so nobody knows which rules apply where — which is worse than either choice applied uniformly.
The judgement to state
The decision is not "is this good architecture" but "is the complexity being protected worth the cost of protecting it?" For a rich domain, comfortably yes. For a CRUD service, comfortably no. Most systems contain both, and applying one answer everywhere is the actual error.