Onion Architecture explained — Building maintainable software

Image by Daniel Rusnok


Image by Monkey User

What is Onion Architecture?

Onion Architecture is an architectural pattern which proposes that software should be made in layers, each layer with it’s own concern.


Domain-driven design (DDD) is the concept that developers and domain experts should use the same names both in code and business domain.
The term was coined by Eric Evans in his book Domain-driven Design: Tackling Complexity in the Heart of Software.

Eric Evans’ book Domain-driven Design: Tackling Complexity in the Heart of Software

Ubiquitous language between domain experts and developers

Domain aware names like, for example, Customer, Product, User, and so on, should mean the same thing both in the domain rules and in the software code.
Both software developers and domain experts should be able to talk in a Ubiquitous Language.

Domain Model

One of the core concepts in DDD is the Domain Model.
A Domain Model is an entity that incorporates behavior and data from some business model.

Layer separation

Onion Architecture proposes three different layers:
- The Domain Layer
- The Application Layer
- The Infrastructure Layer

Domain Layer

The domain layer is the innermost layer of the architecture.

Domain Models

The Domain Models are the core of the Domain Layer. They represent the business models, containing the business rules from it’s domain.

A simple example of a hypothetical banking account model

Domain services

There are some cases where it’s hard to fit a behavior into a single domain model.
Imagine that you are modeling a banking system, where you have the Account domain model. Then, you need to implement the Transfer feature, which involves two Accounts.
It’s not so clear if this behavior should be implemented by the Account model, so you can choose to implement it in a Domain Service.

Value objects

A Value Object is an object that has no identity, and is immutable.
These objects have no behavior, being just bags of data used alongside your models.

A simple value object example

Application Layer

The Application Layer is the second most inner layer of the architecture.

A simple example of an Application Service.
Example of a simple business rule


An Application Service is a piece of code which implements a use case.


If the Application Layer is expected to coordinate operations that involve IO, like loading data from a repository, or sending an email, it should declare some interfaces with the methods it wants to use.
This layer should not worry about implementing these methods, just declaring their signatures.

Interface example


A Data Transfer Object (DTO) is an object that contains data that will be transferred between different layers, in some specific format.
Sometimes you want to transfer data that is not exactly a Domain Model, or a Value Object.

Infrastructure Layer

The Infrastructure Layer is the outermost layer of the Onion Architecture. It’s responsible for implementing all the IO operations that are required for the software.
This layer is also allowed to know about everything contained in the inner layers, being able to import entities from the Application and Domain layers.


A Repository is a pattern for a collection of domain objects.

Views (APIs, CLI, etc)

The parts of your code that expose your application to the outside world are also part of the Infrastructure Layer, as they deal with IO.

The application’s entrypoint — dependency injection

The application’s entrypoint (usually, the main) should be responsible for instantiating all necessary dependencies and injecting them into your code.

Example main()


The Onion Architecture, as any pattern, has its advantages and downsides.

Easy to maintain

It’s easier to maintain an application that has a good separation of concerns. You can change things in the Infrastructure Layer without having to worry about breaking a business rule.
It’s easy to find where are the business rules, the use cases, the code that deals with the database, the code that exposes an API, and so on.

Language and framework independent

The Onion Architecture does not depend on any specific language or framework. You can implement it in basically any language that supports dependency injection.

Dependency injection all the way! Easy to test

By doing dependency injection in all the code, everything becomes easier to test.


However, as there is no silver bullet, there are some downsides.

Cumbersome when you don’t have many business rules

When you are creating a software that does not deal with business rules, this architecture won’t fit well. It would be really cumbersome to implement, for example, a simple gateway using Onion Architecture.

Image by Monkey User

Not so easy learning curve

It can be hard to implement a service using Onion Architecture when you have a database-centric background.
The change in paradigm is not so straightforward, so you will need to invest some time in learning the architecture before you can use it effortlessly.

Pitfalls to avoid

There some pitfalls you should avoid when using this architecture.

Anemic Domain Models

When all your business rules are in domain services instead of in your domain models, probably you have an Anemic Domain Model.

Start by modeling the database

When working with Scrum, you will probably want to break the development of the software into different tasks, so it can be done by different people.

Similar architectures

There are other similar architectures that uses some of the same principles.

Other important things

There are other important things that you need to care when building a maintainable software, such as observability (logs, metrics, tracing), but that’s for another article :)



Software Engineer at Coinsquare

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store