Onion
Onion Architecture organizes a system into concentric layers (like an onion) with the domain model at the center, and all dependencies pointing inward toward it.
The core principles are:
- The domain model is the heart of the system.
- Business logic does not depend on infrastructure, UI, or frameworks.
- External concerns (databases, APIs, UI) live in the outer layers.
- Dependencies are inverted using interfaces.
Onion Architecture Layersβ
From inside β outside:
Domain Layer (Core)β
- Contains entities, value objects, and domain services.
- Holds the most important business rules.
- Has no dependencies on any other layers.
Examples: Order, Customer, Account, Invoice.
Domain Services Layer (Optional but often part of core)β
- Contains domain logic that doesnβt naturally belong to a single entity.
- Still framework-agnostic.
Application Services Layerβ
- Coordinates use cases and workflows.
- Orchestrates domain objects.
- Defines interfaces for infrastructure services (repositories, messaging, email).
Examples: PlaceOrderService, RegisterUserService.
Infrastructure Layerβ
- Implements interfaces defined in the application/domain layers.
- Contains database access, file systems, messaging, external APIs.
Examples: SQL repositories, REST clients, email senders.
Presentation Layer (UI)β
- User interfaces (web, mobile, desktop, API controllers).
- Calls application services.
Dependency Ruleβ
All dependencies must point inward.
That means:
- Infrastructure depends on application/domain.
- Application depends on domain.
- Domain depends on nothing.
Example: Online Ordering System (Onion Architecture)β
Letβs implement a Place Order feature.
1. Domain Layer (Core)β
Entityβ
public class Order {
private int customerId;
private List<Item> items;
public Order(int customerId, List<Item> items) {
this.customerId = customerId;
this.items = items;
}
public double calculateTotal() {
return items.stream().mapToDouble(Item::getPrice).sum();
}
}
Pure business logic, no frameworks, no DB, no UI.
2. Application Services Layerβ
Repository Interface (Defined inward)β
public interface OrderRepository {
void save(Order order);
}
Payment Interfaceβ
public interface PaymentService {
void processPayment(Order order);
}
Application Serviceβ
public class PlaceOrderService {
private final OrderRepository orderRepository;
private final PaymentService paymentService;
public PlaceOrderService(OrderRepository orderRepository, PaymentService paymentService) {
this.orderRepository = orderRepository;
this.paymentService = paymentService;
}
public void placeOrder(int customerId, List<Item> items) {
Order order = new Order(customerId, items);
paymentService.processPayment(order);
orderRepository.save(order);
}
}
Coordinates the workflow using domain objects and abstractions.
3. Infrastructure Layerβ
Database Implementationβ
public class SqlOrderRepository implements OrderRepository {
@Override
public void save(Order order) {
// Save order to SQL database
}
}
External Payment Adapterβ
public class StripePaymentService implements PaymentService {
@Override
public void processPayment(Order order) {
// Call Stripe API
}
}
Implements interfaces but depends on application/domain layers.
4. Presentation Layer (UI / API)β
@RestController
public class OrderController {
private final PlaceOrderService placeOrderService;
public OrderController(PlaceOrderService placeOrderService) {
this.placeOrderService = placeOrderService;
}
@PostMapping("/orders")
public void placeOrder(@RequestBody OrderRequest request) {
placeOrderService.placeOrder(request.getCustomerId(), request.getItems());
}
}
Handles HTTP and delegates business logic to the application layer.
How the Flow Worksβ
User β UI β Application Service β Domain β Infrastructure β External Systems
Even though execution flows outward, dependencies flow inward.
Advantages of Onion Architecture
- Strong domain focus
- Business logic independent of frameworks
- High testability
- Infrastructure can be swapped easily
- Encourages SOLID principles
Disadvantages
- More interfaces and abstraction
- Learning curve
- Overkill for very small/simple projects
Onion vs Clean vs Hexagonalβ
| Feature | Onion | Clean | Hexagonal |
|---|---|---|---|
| Structure | Concentric rings | Concentric rings | Core + ports/adapters |
| Core focus | Domain model | Business rules (entities + use cases) | Core logic |
| Ports/Interfaces | In application layer | Input/output boundaries | Explicit ports |
| Dependency direction | Inward | Inward | Inward |