Skip to content

Onion Architecture #3266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions onion-architecture/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
title: "Onion Architecture in Java: Building Maintainable and Scalable Applications"
shortTitle: Onion Architecture
description: "Learn how Onion architecture helps organize your code for better separation of concerns, testability, and long-term maintainability. Ideal for backend developers and software architects building enterprise applications."
category: Architectural
language: en
tag:
- Clean Architecture
- Layered Design
- Dependency Rule
- Maintainability
---

## Also known as

* Ports and Adapters
* Clean Architecture (variant)

## Intent of Onion Architecture

The Onion Architecture aims to address common problems in traditional layered architecture by enforcing a clear separation of concerns. It centralizes domain logic and pushes infrastructure concerns to the edges, ensuring that dependencies always point inward toward the domain core. This structure promotes testability, maintainability, and flexibility in adapting to future changes.

## Detailed Explanation of Onion Architecture with Real-World Example

Real-world example

> Imagine a fortress with multiple protective walls around a valuable treasure. The treasure (core domain) is heavily guarded and never exposed directly to the outside world. Visitors (like external systems or users) first pass through the outermost gates (infrastructure), where guards verify their identity. Then they proceed inward through other layers like application services, each with stricter checks, before finally reaching the treasure, but only through clearly defined and controlled pathways. Similarly, in Onion Architecture, the most critical business logic sits protected at the center. External concerns like databases, APIs, or user interfaces are kept at the outer layers, ensuring they cannot tamper directly with the core. Any interaction must pass through proper services and abstractions, preserving the domain’s integrity.

In plain words

> Onion Architecture builds your application like an onion: the important core (business rules) stays safe inside, while things like UI and databases are kept outside. No matter how the outer layers change, the core stays stable and unaffected.

jeffreypalermo.com says

> The fundamental rule is that all code can depend on layers more central, but code cannot depend on layers further out from the core. In other words, all coupling is toward the center. This architecture is unashamedly biased toward object-oriented programming, and it puts objects before all others. The Onion Architecture relies heavily on the Dependency Inversion principle.

## Programmatic Example of Onion Architecture in Java

The Onion Architecture in Java structures code into concentric layers where the core business logic is independent of external concerns like databases, frameworks, or UI. This is achieved by depending on abstractions rather than concrete implementations.
It ensures that the domain remains unaffected even if the technology stack changes, making applications highly maintainable and testable.

Let's take a look at a simple `OrderService` example in an Onion Architecture style:

1. Core Domain Layer (domain module)

```java
public class Order {
private String orderId;
private List<String> items;

public Order(String orderId, List<String> items) {
this.orderId = orderId;
this.items = items;
}

public String getOrderId() {
return orderId;
}

public List<String> getItems() {
return items;
}
}
```
2. Application Layer (application module)

```java
public interface OrderRepository {
void save(Order order);
}
```

```java
public class OrderService {
private final OrderRepository orderRepository;

public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}

public void placeOrder(Order order) {
orderRepository.save(order);
}
}
```
3. Infrastructure Layer (infrastructure module)

```java
public class InMemoryOrderRepository implements OrderRepository {
private Map<String, Order> database = new HashMap<>();

@Override
public void save(Order order) {
database.put(order.getOrderId(), order);
System.out.println("Order saved: " + order.getOrderId());
}
}
```
4. Entry Point (App)

```java
public class App {
public static void main(String[] args) {
OrderRepository repository = new InMemoryOrderRepository();
OrderService service = new OrderService(repository);

List<String> items = Arrays.asList("Book", "Pen");
Order order = new Order("ORD123", items);

service.placeOrder(order);
}
}
```

- The Domain Layer (Order) has no dependencies.

- The Application Layer (OrderService, OrderRepository) defines operations abstractly without worrying about how data is stored.

- The Infrastructure Layer (InMemoryOrderRepository) provides actual data storage.

- The App Layer wires everything together.

## When to Use Onion Architecture in Java

* Enterprise Applications: Perfect for large systems where business logic must remain stable despite frequent changes in technology or external dependencies.

* Highly Maintainable Systems: Useful when you anticipate frequent feature additions or technology upgrades (e.g., switching from MySQL to MongoDB).

* Test-Driven Development (TDD): Ideal for systems that require extensive unit and integration testing, as the domain can be tested independently.

* Microservices: Helps keep microservices clean, with clear separation between core business rules and communication protocols like REST or gRPC.

## Real-World Applications of Onion Architecture in Java

* Banking and Financial Systems: Where strict control over domain rules and processes is essential, even when interfacing with different databases, APIs, or UIs.

* E-commerce Platforms: Separates critical order, payment, and inventory logic from external services like payment gateways and user interfaces.

* Healthcare Applications: Ensures that patient management, diagnosis, and treatment core logic stays unaffected by changes in reporting tools, hospital systems, or regulatory APIs.

## Benefits and Trade-offs of Onion Architecture
Benefits:

* Separation of Concerns: Clear separation between business logic and technical concerns like databases, UI, or external services.

* High Maintainability: Core business rules can evolve independently of infrastructure or interface changes.

* Enhanced Testability: Inner layers can be unit-tested easily without setting up external dependencies.

* Adaptability: Easier to swap out technologies without touching the core domain.

Trade-offs:

* Initial Complexity: Requires careful design of layers, abstractions, and dependency rules upfront.

* More Boilerplate Code: Interfaces, DTOs, and mappers add to codebase size and complexity.

* Learning Curve: New developers might take longer to understand the structure if they’re not familiar with layered architecture principles.

## Related Architectural Patterns in Java

* [Hexagonal Architecture](https://www.geeksforgeeks.org/hexagonal-architecture-system-design/)
* [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)

## References and Credits

* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
* [Onion Architecture by Jeffery Palermo](https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/)
* [Onion Architecture - Medium article](https://medium.com/expedia-group-tech/onion-architecture-deed8a554423)


73 changes: 73 additions & 0 deletions onion-architecture/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).

The MIT License
Copyright © 2014-2022 Ilkka Seppälä

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.4</version>
<scope>test</scope>
</dependency>
</dependencies>

<artifactId>onion-architecture</artifactId>

<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>com.iluwatar.onionarchitecture.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
Loading
Loading