Stem in Onion Architecture or Fallacy of Data Layer
Stem in Onion Architecture or Fallacy of Data Layer
Ogres are like onions, they have layers. And, apparently, so are big data applications. Read on to learn about Onion Architecture.
Join the DZone community and get the full member experience.Join For Free
If you are still wondering why the data layer (infrastructure) in Onion Architecture is on the outmost layer, 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 the Application Core and not the other way around.
Data access is external and depends on the Application Core and not the 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 the Onion Architecture aside 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 exist and function on its 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 two layers only, where every layer has exactly one component and one corresponding class to each component (Object-Oriented Design).
UML class diagram with two classes at different layers.
The UML diagram shows two classes, where Foo (layer 'top') depends on Bar (layer 'bottom'). Well, we don't like such implementations to have 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 the first place.
Software Architecture always tends to erode - a process where the actual implementation diverges from the original design. Here's an example of this situation:
UML class diagram with a new layer and shifted implementation.
Uh oh, moving the Bar class from the layer 'bottom' to the new layer 'topmost' reduces Mobility and creates the 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 this info about layers somewhere, 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 details 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 the one we saw a moment ago, where a classic Multi-Layered Architecture eroded and dependencies were violated? Think about it.
As demonstrated before, an Abstraction Pattern is not a prerequisite to the Multi-Layered Architecture (but, rather, an implementation detail; its recommended to have this in your architecture), 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 the diagram of the Onion Architecture causes cognitive dissonance.
You'll find that the Onion Architecture, in particular, solves the problem of having the Data Layer at the very 'bottom' like this:
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 the '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 able to function as an independent layer(s). 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 the 'Bad Onion' with the only difference being the implementation details. Regardless of whether 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 the other middle layer(s), at least from the perspective of layered architecture. I would say that the original diagram of the Onion Architecture attempts to embrace a high-level architecture overview and a lower level component design, which is not a trivial task. Personally, I want to remove the 'Infrastructure' from that view to emphasize the idea that the Domain Model matters and everything else is not that relevant. As an alternative, I think it's better to show the Onion from the 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.
I 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.
Published at DZone with permission of Serge Semenov . See the original article here.
Opinions expressed by DZone contributors are their own.