This is part of a series, start here!
It helps to think of this as an update to Ports & Adapters that brings more fine-grained control over the Application Core.
Bringing back the layers
When going over Ports & Adapters we saw that it sort of got rid of the layers. More specifically, it only (implicitly) left two of them:
- An external layer (with Adapters and the relevant infrastructure).
- An internal one (with pretty much everything else).
As you might imagine, especially in bigger applications, having most of the code bundled in a single layer can get… Complicated.
That’s where Jeffrey Palermo’s Onion Architecture shines.
In a nutshell, it’s a more detailed specification regarding how to organize what remains of our code after defining where the boundaries (Ports & Adapters) are.
Let’s have a look at these layers, starting from the core of the Onion.
Domain Model
This would be our core business Entities, enriched with their corresponding rules and logic.
[…] the state and behavior combination that models truth for the organization.
1
This part of the system should change only if the most essential business rules change, which doesn’t usually happen (if ever).2
Domain Services
Similar to how Interactors managed the interactions between Entities in the EBI Architecture, all Domain logic involving multiple Models will go here.
Whatever business logic doesn’t fit the scope of a single Entity (or value object for that matter) belongs here.
Application Services
Like Domain Services orchestrate the interactions between multiple Models, Application Services orchestrate the interactions between multiple Domain Services.
Here we’ll also find the Ports and use cases definition from Ports & Adapters, right at the boundary of our application.
Inwards dependency
As you can see, this design enforces the same ‘direction of dependency’ as Ports & Adapters, only this time it is also applied to our own.
Not only does our infrastructure depend on our Domain (and not the other way around), but the layers within our domain also depend on whatever layers lay beneath them.
This way, we end up with an independent core that can (and should) be compiled, executed and tested independent of its outer layers.
We couple towards the center. From Palermo’s original post. ↩ When these changes occur, they are usually accompanied by structural, organizational changes. ↩Footnotes