Onion Architecture

🗓️
🔄
3 min

It helps to think of this as an update to Ports & Adapters that brings more fine-grained control over the Application Core.

Bring back the Layers

When we talked about Ports & Adapters we mentioned that it sort of got rid of the layers. More specifically, it left us with just two of them.

We only had an external layer (with our Adapters and the relevant infrastructure), and an internal one (with pretty much everything else).

As you might imagine, especially in enterprise applications, having most of our code bundled in a single layer can be… Troublesome.

That’s exactly what our Onion is here to solve. To keep it simple: It’s a more detailed specification regarding how to organize what remains of our code after defining where the boundaries (Ports & Adapters) are.

onion-architecture

Let’s have a look at our new layers, from the inside-out.

Domain Model

Our core business Entities, enriched with their corresponding rules and logic (once again, anemic Entities are not welcome).

Anything specific to our Domain and/or business logic that does not require Entities to interact with one another goes here.

Hopefully, this piece of our code will change only if our most basic business rules change.

Domain Services

Similar to how we reasoned about Interactors when talking about EBI, all Domain logic involving multiple Entities will go here.

So again, whatever business and\or Domain logic doesn’t fit the scope of one single Entity (or value object for that matter) belongs here.

Application Services

The use case orchestration layer.

It has two distinct responsibilities:

  1. Orchestrate the required Domain Services and/or Models to fulfill the use cases of the application.
  2. Present interfaces for the infrastructure to implement

More or less in line with Ports & Adapters, here we’ll find use cases as well as ports (or interfaces for the infrastructure).


Inwards dependency

As you can see, this architecture will enforce the same ‘direction of dependency’ as Ports & Adapters, only this time it applies to our own code as well.

It’s not just that our infrastructure depends on our Domain and not the other way around. Now also the layers within our domain depend on whatever layers lay beneath them (not necessarily only its direct inner layer).

This way, we end up with an independent core that can (and should) be compiled, executed and tested with no specific infrastructure.

This architecture forces us to couple towards the center.


Other posts you might like