Center
Domain entities, value objects, policies, and invariants that should outlive tooling decisions.
Onion Architecture emphasizes concentric dependency direction. The domain model sits in the center. Around it come application services and use cases. Infrastructure and interfaces stay outside. It overlaps strongly with hexagonal and clean architecture, but its teaching strength is how clearly it visualizes dependency direction.
In an order domain with returns, refunds, shipment policies, and pricing rules, the most stable knowledge is often business knowledge. Onion architecture protects that center from web, database, and framework churn.
The onion metaphor makes architecture concrete. The core contains the most stable business concepts. Each outer ring depends inward, never the other way around. This creates a very explicit mental model for dependency direction and often pairs naturally with rich domain modeling.
Where hexagonal architecture talks about ports and adapters, onion architecture talks more visually about the center and the rings around it. In practice, many systems mix the language of both.
Domain entities, value objects, policies, and invariants that should outlive tooling decisions.
Use cases and application orchestration that coordinate the domain without owning the domain’s meaning.
Web controllers, databases, queues, and external integrations that should remain replaceable.
Order, Money, and ShipmentPolicy. Around that sits PlaceOrderUseCase. Around that are controllers, repositories, brokers, and message adapters. All code should depend inward toward the business center.
package org.javaomnibus.ecommerce.domain;
public final class ShipmentPolicy {
public boolean canShip(Order order) {
return order.isPaid() && !order.isCancelled();
}
}
package org.javaomnibus.ecommerce.application;
public final class ShipOrderUseCase {
private final ShipmentPolicy shipmentPolicy;
private final ShipmentPort shipmentPort;
public ShipOrderUseCase(ShipmentPolicy shipmentPolicy, ShipmentPort shipmentPort) {
this.shipmentPolicy = shipmentPolicy;
this.shipmentPort = shipmentPort;
}
}