A team applies hexagonal architecture and finds it adds ceremony without benefit. When does the pattern earn its cost?
Show the full answer Hide the answer
When it earns its cost
- When the external dependency is genuinely replaceable and replacement is plausible. A payment provider, a storage backend, a search engine, a third-party API with a contract renewal date. The port is what makes the swap a project rather than a rewrite.
- When the domain logic is complex and worth testing without I/O. A port with an in-memory adapter makes domain tests fast and deterministic, which for a system with substantial business rules is a large practical benefit.
- When several adapters genuinely exist — the same domain driven by an HTTP API, a message consumer and a scheduled job.
When it is ceremony
When the adapter is one-to-one with the thing it adapts and will never be replaced. A repository interface with one implementation, mapping fields one-to-one onto a database the team will never change, is indirection that adds a file and obscures the query.
When the domain has no logic, and the "domain layer" is a pass-through between a controller and a repository. That is a large fraction of CRUD applications, and applying the pattern there produces four layers restating one concern.
The diagnostic
Count the files a small change touches. Adding one field should touch the schema, an index if performance requires it, and a policy if the field needs one. If it touches nine files across four layers, the layers are restating one concern at four altitudes rather than separating different concerns — which is layering applied by convention rather than by responsibility, and it taxes every feature for the life of the system.
Also check the opposite direction: if a cross-cutting change is cheap and a single-field change is expensive, the separation is inverted.
The middle position
Apply it at the boundaries that matter and go direct elsewhere. Ports for the third-party integrations, the datastore if replacement is plausible, and the entry points. Direct access for everything internal.
A pattern applied uniformly is a convention, not a design decision — and conventions applied without regard to whether they earn their cost are the most common source of accidental complexity in otherwise well-intentioned codebases.