Ask
25

I cannot see what hexagonal architecture gives me that a layered one does not

In the hexagonal description, the application core has incoming ports called by incoming adapters, implemented by services, which in turn use outgoing ports implemented by outgoing adapters.

When I write that out, it looks like a presentation layer, a service layer and a data access layer with different words. Ports are interfaces, adapters are implementations, and layered architecture also has interfaces between layers.

Is there a real structural difference, or is this mainly a vocabulary and a diagram shape? I am not trying to be dismissive — I would like to know what I would actually build differently.

4 answers Share
Report

Answering anonymously — a moderator will review it first.

  • @architect_ayla · 3d ago

    The concrete difference in what you build, since that is what you asked:

    Where the interface lives. This is the tell. In layered code, OrderRepository is defined in the persistence package. In hexagonal, it is defined in the domain package and implemented in persistence. Same file count, opposite meaning.

    What the core is allowed to import. In hexagonal the core imports no framework, no ORM, no HTTP library, no database driver. That is a rule you can actually enforce with a build check, and enforcing it is where most of the benefit comes from.

    Symmetry of the outside. Layered architecture treats the top and the bottom differently — user interface above, database below. Hexagonal treats them the same: an incoming HTTP request and an incoming message queue event are both just adapters, and a database and a payment provider are both just adapters. That is why it is drawn as a shape with sides rather than a stack.

    Whether that is worth the ceremony depends on whether you will ever swap anything. If your database will be the same one in ten years, the practical benefit is mostly testability.

    26
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @maintainer_mika · 5d ago

    The honest answer to "is this mainly vocabulary" is: in most codebases that claim it, yes.

    I have reviewed a lot of projects with a hexagonal folder structure whose domain layer imports the ORM's base entity class, or whose "port" is a copy of the repository's method signatures written after the repository. Those are layered systems in hexagonal costume, and they carry the cost — extra interfaces, extra mapping, extra files — without the benefit.

    The test that separates them is blunt and worth running on your own code: can you compile and unit test the core with the database, web framework and every external client removed from the build? If yes, you have the architecture. If it does not even compile, you have the diagram.

    20
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @ports_adapters_pia · 4d ago

    There is one real structural difference and it is entirely about which way the dependencies point.

    In a classic layered architecture, each layer depends on the one below it. Presentation depends on services, services depend on data access, data access depends on the database driver. The dependency arrows all run downward, which means your business logic depends on your persistence layer.

    In hexagonal, the core depends on nothing. It defines an interface describing what it needs — "something that can store an order" — and the database adapter implements that interface. The arrow now points inward, from the adapter to the core, which is the reverse of the layered version.

    That single change is the whole idea, and everything else follows from it. If your layered code has the service layer importing a repository interface that lives in the data access layer, you have layers. If the interface lives with the business logic and the data access code implements it, you have hexagonal — regardless of what you call the folders.

    30
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @avionics_arto · 2d ago

    One reason to care that is often understated: it is much easier to test the core when nothing external is reachable from it.

    Not faster tests — although that too — but tests that express behaviour rather than plumbing. When the core depends only on interfaces it defined, a test provides simple in-memory implementations and the test reads as a statement about the business rule. When the core depends on the persistence layer, the same test needs a database or a heavy mocking framework, and it ends up asserting about calls rather than about outcomes.

    That difference compounds over years more than any swap-the-database argument, which almost never happens.

    15
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report