Skip to main content

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​

FeatureOnionCleanHexagonal
StructureConcentric ringsConcentric ringsCore + ports/adapters
Core focusDomain modelBusiness rules (entities + use cases)Core logic
Ports/InterfacesIn application layerInput/output boundariesExplicit ports
Dependency directionInwardInwardInward