Stem in Onion Architecture or Fallacy of Data Layer

Serge Semenov
6 min readMay 28, 2018

--

If you are still wondering why the data layer (infrastructure) in Onion Architecture is on the outmost side, and you find it hard to explain the concept to your colleagues or perhaps to yourself, then this article is for you. The short answer is “it’s not”. The full answer follows.

Back in 2008 Jeffrey Palermo shared with the world his view of a Domain Driven Design which he called “Onion Architecture” with the following diagram, where the most important question is “How do I implement it if my business logic is not allowed to use any data layer (infrastructure)?”

Data access is external and depends on Application Core, and not other way around.

This can be very baffling to explain even for very experienced software developers, because when it comes to the point when you have to write the actual code, you simply might not know how to invert such layering compared to classic Multi-Layered Architecture.

Let’s put aside the Onion Architecture for a moment, and quickly review standard Three-Layer Architecture.

Standard 3-layer architecture. Shapes represent various components.

The diagram above represents various component grouped into layers, where the rule is that a layer ‘above’ cannot reference a layer ‘below’, what translates to “A component in a referenced layer must not depend on a component from a referencing layer, and must function independently from any referencing layer”. The key to understanding here is that a ‘Data’ layer must existing and function on it own, then a ‘Logical’ layer exists and functions on its own as well, but only with coupling to the underlying ‘Data’ layer, and so on.

Now let’s take a random imaginary implementation of such architecture with 2 layers only, where every layer has exactly one component and one corresponding class to each component (Object-Oriented Design).

UML class diagram with 2 classes at different layers.

The UML diagram shows 2 classes, where Foo (layer ‘top’) depends on Bar (layer ‘bottom’). Well, we don’t like such implementation with tight coupling, so we use the Dependency Inversion Principle along with the Abstraction Pattern to make it more flexible.

UML class diagram with introduced abstraction.

Great, now you have a more loose coupling, and you can swap out the implementation of IBar with anything else, what is extremely useful for testing in first place.

Software Architecture always tends to erode — a process where the actual implementation diverges from the original design, what can create such situations:

UML class diagram with a new layer and shifted implementation.

Uh oh, moving Bar class from the layer ‘bottom’ to the new layer ‘topmost’ reduces Mobility and creates a danger of having circular references between layers. From a Multi-Layered Architecture standpoint, it’s a violation, where you need to re-think the design instead to make sure that Bar does not depend on Qux from the layer ‘above’.

Now stash the topic about layers and let’s jump back to the Onion Architecture. You can find a lot of examples online on how to implement it, where they show this clever trick:

Implementation detail of the Onion Architecture.

Aha! Now it makes sense, to focus on the Domain Model and make it in the center of everything, we just need to define an abstract repository interface at the lower level and provide its concrete implementation at the topmost layer (the Infrastructure). This should allow us to run exactly the same application logic revolving around a domain model in any environment. Genius!

But wait. Isn’t it a similar situation as we saw a moment ago in which a classic Multi-Layered Architecture eroded and dependencies have been violated? Think about it.

As demonstrated before an Abstraction Pattern is not a prerequisite to the Multi-Layered Architecture (but rather an implementation detail; recommended to have), and dividing a component into a definition and its implementation does not magically create a new layer. This brings us to the most important question: “Is Infrastructure an actual layer after all?”. Also: “Can the Application Core layers function without the Infrastructure layer?”. No wonder if the diagram of Onion Architecture causes a cognitive dissonance.

You can find that Onion Architecture, in particular, solves the problem of having the Data Layer at the very ‘bottom’ like this:

‘Bad Onion’.

But let’s quickly draw a sample diagram of a ‘Good Onion’ vs a ‘Bad Onion’ through the ‘prism’ of Multi-Layered Architecture:

Simplified Onion (Good) vs Multi-Layered (Bad) Architectures.

Even though the ‘implements’ arrow from Repository class in ‘Good Onion’ case points downwards to the IRepository, the Application / Domain are still indirectly dependent on the infrastructure (the red arrow), thus leaving them incomplete and not being able to function as an independent layer. Hence the Infrastructure here merely represents an implementation piece of Application / Domain layer(s) shifted upwards, what once again would be considered as a violation in Multi-Layered Architecture. That means the ‘Good Onion’ is exactly the same as ‘Bad Onion’ with the only difference — implementation details. Regardless if a component has an abstraction or not, all its parts should belong to the same layer, and it’s not feasible to have a logical repository without direct or indirect dependency to a data layer in a reasonable manner.

So is ‘Infrastructure’ a layer? Yes. Is it the topmost one? No. It’s a mixture of a standard Data layer with implementation details of other middle layer(s), at least from a perspective of layered architecture. I would say that the original diagram of Onion Architecture attempts to embrace a high-level architecture overview and a lower level component design, what is not a trivial task. Personally, I want to remove the ‘Infrastructure’ from that view to emphasize the idea that Domain Model matters and everything else is not that relevant. As an alternative, I think it’s better to show the Onion from a side.

Original ‘top’ view without infrastructure vs. an alternative ‘side’ view.

With the side view, you can draw an imaginary boundary where the physical onion ends — its ‘stem’ represents abstractions of the Application Core, which connects through ‘roots’ (Dependency Inversion) to the actual implementation (‘ground’) without any additional application logic. Now you can ‘replant the onion to a better soil’ when needed.

Hope this adventure helped to understand better the Onion Design, dispels misrepresentation of the data layer, and will help to assess all pros and cons compared to classic layering in future considerations when you decide to implement such Domain-Driven Design that helps to control coupling of various components in large business applications.

--

--

Serge Semenov

‘I believe in giving every developer a superpower of creating microservices without using any framework’ — https://dasync.io 🦸‍♂️