Hexagonal
Best when the team benefits from explicit port and adapter vocabulary around integrations and use-case entry points.
These three styles are often treated as synonyms. In practice, they share a strong family resemblance: protect the core, keep dependencies pointing inward, and isolate infrastructure. The differences are mostly about emphasis and teaching model, but that difference can still matter when teams are learning or reviewing architecture.
Teams waste time arguing about labels when they should be discussing dependency direction, port design, domain richness, and adapter placement. This comparison helps move that conversation back to substance.
Hexagonal architecture emphasizes ports and adapters. Onion architecture emphasizes concentric dependency direction with the domain in the center. Clean architecture emphasizes the dependency rule and the separation between entities, use cases, interface adapters, and frameworks. The overlap is strong because all three try to keep business policy from depending on infrastructure.
The most helpful question is usually not which term is most correct. It is which teaching lens will help your team apply the boundary rules consistently in the codebase you actually have.
Best when the team benefits from explicit port and adapter vocabulary around integrations and use-case entry points.
Best when the domain-centered visual model helps the team reason clearly about inward dependencies.
Best when the dependency rule and use-case orientation are the strongest teaching anchors for the team.
| Style | Strongest teaching emphasis | Typical Java payoff |
|---|---|---|
| Hexagonal | Ports and adapters | Clear integration seams and replaceable infrastructure |
| Onion | Domain at the center | Stronger domain-centric thinking and inward dependency intuition |
| Clean | Dependency rule and use cases | Explicit business policy separation from frameworks |
package org.javaomnibus.ecommerce.application;
public final class CapturePaymentUseCase {
private final PaymentPort paymentPort;
private final OrderStore orderStore;
public CapturePaymentUseCase(PaymentPort paymentPort, OrderStore orderStore) {
this.paymentPort = paymentPort;
this.orderStore = orderStore;
}
}
// Hexagonal lens: PaymentPort and OrderStore are output ports.
// Onion lens: the use case sits outside the domain center but still points inward.
// Clean lens: the use case stays independent from controllers, frameworks, and gateways.