Ports and Adapters vs Layered Architecture
Fast recall
Quick revision
Core answer
Direction of dependencies, why hexagonal puts domain at the center, when the extra abstraction pays off vs when layered is enough.
Saying 'hexagonal is just layered with extra interfaces' — the dependency direction is fundamentally different.
Discuss migration path from layered to hexagonal, how Spring still fits naturally with hexagonal (in adapters), and the cost-benefit at different team / project sizes.
Interview-ready guide
Interview answer
The two mental models
Layered: stack of horizontal layers. Controller → Service → Repository → Database. Dependencies flow downward; database at the base; domain depends on JPA/framework. Whole stack built around the database.
Hexagonal: concentric, domain-centric. Adapters (outside) → Ports (interfaces) → Domain (center). Dependencies flow inward; database is just one adapter on the outside; domain depends on nothing. Whole stack built around the domain.
The real difference is dependency direction, not visual shape.
Concrete diff
Layered: @Service has @Autowired OrderRepository which is a Spring Data JpaRepository. Returns JPA entity. Domain coupled to JPA.
Hexagonal: PlaceOrderService is plain Java, depends on OrderRepository which is a port (plain interface). Domain Order is a record. JpaOrderRepository sits in the adapter layer, implements the port, converts between domain Order and JPA entity.
What hexagonal gives you
- Framework independence: no @Entity, @Transactional, @Service in the domain. Swap Spring → Quarkus, JPA → Mongo — core stays put
- Pure-unit testability: plain JUnit + in-memory adapter; no @SpringBootTest, no Testcontainers; milliseconds
- Multiple input adapters share business logic: HTTP + Kafka + CRON all call PlaceOrderUseCase
- Explicit boundaries: ports document what the core needs from infrastructure
- Migration safety: replace JPA with Mongo = replace adapter; domain logic untouched
What layered gives you
- Less ceremony: fewer interfaces, mappers, packages. For a 5-endpoint CRUD it's half the code
- Familiarity: Spring tutorials default to layered
- Direct framework leverage: Spring Data magic methods right at the service
- Faster prototyping: POC ships in hours
When to choose which
Layered for: CRUD-heavy thin business logic, one input adapter, small team/service, speed > flexibility.
Hexagonal for: rich domain logic, multiple input adapters share use cases, testability without infrastructure required, long-lived service likely to outlive current framework/DB, pairing with DDD.
Migration layered → hexagonal
- 1Extract use case interfaces from services
- 2Move pure domain logic into framework-free classes
- 3Define repository ports; existing Spring Data interfaces become JpaXxxRepository, adapter implements port
- 4Mappers (DTO ↔ domain, JPA entity ↔ domain) into adapter packages
- 5Add unit tests with in-memory ports
Common mistakes
- Calling layered-with-interfaces 'hexagonal' — only hexagonal if core defines the contract
- Duplicating domain and JPA entities dogmatically — sometimes one record works as both
- Hexagonal in tiny services — 3 endpoints don't need 12 ports
- Thinking hexagonal is anti-Spring — Spring lives in adapters and wires the core
- One port per repository method — group cohesive behavior
Recommendation
Default to layered for CRUD-heavy Spring Boot services where business logic is thin, the team is small, and time-to-prototype matters. Reach for hexagonal when the domain is rich enough to deserve protection from infrastructure, when multiple input adapters share use cases, when fast pure-unit tests of business logic are critical, or when you're pairing with DDD. The two are not enemies — hexagonal still uses Spring; Spring just lives in the adapters, not the core. Migration is incremental: extract use case interfaces, move pure logic out, introduce ports one at a time.
Ports and Adapters vs Layered Architecture
Direct Answer
Layered architecture organizes by technical layer (controller → service → repository) with the database at the bottom and the domain dependent on the framework above it. Ports and Adapters (Hexagonal) puts the domain at the center with interfaces (ports) defining what it needs; adapters on the outside implement them. Layered's dependency arrow flows toward the database; hexagonal's flows toward the domain. Hexagonal gives you framework-independence and pure-unit testability; layered is simpler for CRUD apps where those benefits aren't worth the extra abstraction.
Common Mistakes & Pitfalls
Saying 'hexagonal is just layered with extra interfaces' — the dependency direction is fundamentally different.
Check your understanding
Answer these without looking back at the article.
- How do you start migrating a layered Spring Boot app to hexagonal incrementally?
- Where do transactions live in hexagonal — in the core or in the adapter?
- Show how the same use case can be triggered from HTTP and Kafka in hexagonal.
- Is there a downside to hexagonal? When would you regret using it?
Related interview prep categories
1Want a guided curriculum order?
These modules are also sequenced in our flagship backend interview track — open it when you prefer an ordered path instead of browsing by topic.