Architecture

Hexagonal vs layered architecture

Both compile. Both ship. The difference doesn’t show on day one. It shows on year three, when the business logic is welded to the framework and the database and nobody can change one without breaking the others.

What “layered” gives you

Controllers, services, repositories: fast to start, familiar to everyone. But the domain ends up importing JPA, Spring and the web layer, so your business rules can’t be tested, reused or reasoned about without the whole stack around them.

What it costs at year three

When the domain imports JPA, Spring and the web, a change in any of them reaches into your business rules. You cannot unit-test a rule without booting the whole stack, a new column ripples outward, and onboarding means understanding everything at once. Layered architecture rarely fails in the design review; it fails at the 200th entity, under a deadline, when the coupling is already everywhere.

What hexagonal changes

Ports and adapters: the domain has zero framework dependencies, and the database, the framework and the delivery mechanism become swappable details behind ports. Each entity is one self-contained vertical slice:

  • Domain — the entity and its rules, plain Java with no Spring, JPA or web imports. Testable in isolation.
  • Application — the use cases, reaching the outside world only through ports (interfaces the domain owns).
  • Infrastructure — the adapters behind those ports: persistence, the HTTP controller, messaging. Swappable without touching the core.

The boundary is enforced, not hoped for

This is not a naming convention you hope people respect. ArchUnit rules ship inside the test suite and fail the build if the domain reaches for the framework, or a layer imports across a boundary it should not. The hexagon stays a hexagon, build after build.

If you know Spring Modulith

Same goal, reached a different way. Spring Modulith enforces module boundaries inside a monolith; here every entity is already a self-contained slice with the domain isolated, and ArchUnit keeps those boundaries honest in CI. The same modules ship as a modular monolith or as microservices, so you get the boundary discipline without committing to one runtime shape up front.

Why generators usually produce layered CRUD

Because it’s the path of least resistance, and AI-assisted teams drift toward it under deadline pressure. AlveoForge does the opposite by construction: real hexagonal with vertical slicing, the same way every time, so the architecture can’t quietly rot into MVC with hexagonal folder names.

See a backend running →Prefer to talk? Book a call →
← Back to the overview