26

One thing beforehand: I arrive from an N-layered background.

I have now spent quite a bit time getting my head around Onion Architecture and related Domain Driven concepts such as Hexagonal Architecture reading resources like Jeff Palermo's series of blog posts, Mark Seemann's contribution from a DI-perspective, "Onion-izing your achitecture", and "The clean architecture".

What all of these articles have in common is that they claim the following points:

  • Focus is kept around the domain model of the business use case
  • Looser coupling between layers by emphasizing the Dependency Inversion Principle
  • Increased independence of external infrastructures such as frameworks, data persistence, UI
  • Better testability / maintainability

Well, that all sounds incredibly nice and those diagrams look sweet as well. But the question that arises for me: Isn't all of that achieved by merely adding façades to my traditional N-layered architecture?

  • Each layer just knows the abstractions of the layer below
  • The concrete implementations can be kept internal to each layer and hence are in the same place as the abstractions
  • The implementation details can be easily swapped out since they are internal to the layer and should not affect the rest of the application

Please help me understand the true advantages of a domain-centric architecture.

Thanks in advance!

Ben Sch
  • 2,639
  • 4
  • 17
  • 23
  • What do you mean by: "The concrete implementations can be kept internal to each layer and hence are in the same place as the abstractions"? Onion Architecture is about to push implementation out as far as possible. – Dzendo Jan 19 '15 at 16:42
  • Yea, I am aware of that. But I think this can also lead to confusions, especially for new developers. If they only work with the abstractions, but the actual implementations are in a complete different place / layer. That's why I pointed out, that with N-layerd architecture with facades, the abstractions would be in the same place as their implementations. – Ben Sch Jan 19 '15 at 20:20

6 Answers6

8

Adding facades are really the first step in building an onion architecture out of an n-layered architecture. So, yes, you can get many of the benefits right away.

Testing is still problematic as you need to invert the dependency control. Controlling what has the facade is pointing to needs to move to the consumer, not the provider. This allows that consumer to swap things out for testing, or to change implementations without the provider having to know about it.

Rob Conklin
  • 6,925
  • 1
  • 14
  • 18
  • Thanks for that response. It helped me clear things up a bit more. Could you perhaps elaborate a bit more on the issues concerning testing? My classes still just specify their dependencies through interfaces in their constructors. The concrete implementations will be injected via an IoC container. Why can't the test assembly inject mock objects of these interfaces into the class under test just alike? It has access to the interfaces of the assembly as well. – Ben Sch Jan 21 '15 at 15:22
  • If your outer layers are already using an IOC to inject their dependencies, then you have already achieved onion architecture. If you have the inner layers providing their own IOC, you will need to make each of them aware of how they are going to be tested/used. This is the advantage of moving the control to the client (outer layers). It allows the inner layers to be ignorant of how they are used, and lets the outer layers deal with that. – Rob Conklin Jan 21 '15 at 16:48
  • 5
    Well, it's not "really" an onion architecture as I understood it, since in my solution Presentation Layer only references (the interfaces of) BLL which in turn only references (the interfaces of) DAL. In Onion, as promoted by Jeff Palermo, PL could reference the data abstraction interfaces directly as well, correct? – Ben Sch Jan 21 '15 at 17:12
  • @BenSch, I'm fairly new to this concept myself but from my understanding of it, with the setup you just described in the last comment here, you could inject a DAL class... if that is true, then your setup could be considered closer to onion architecture than layered architecture. – Ian Kirkpatrick Apr 21 '20 at 12:19
3

I'm sharing the same opinion as yours, We're planning to kick off a new project and one of my coworkers suggested the onion architecture, but after documenting a bit about it I was a little bit confused, because (for me!) the fact that each client layer must depend only on the abstraction of the used layer is a matter of best practice that must always be in mind whatever architecture we're planning to use, DI is a "Principle" not an architecture so I couldn't realize how just using the N-Layered architecture with "good OO Principles" make it a new architecture?

In this way we'll end by hundreds of new architectures just by combining all OO Principals and Go4 patterns to the Entreprise Application Architectures.

A77
  • 175
  • 1
  • 6
3

Though it is very old question, but would like to add something.

Onion Vs Layered

This article explains it clearly why layered is not preferable but if you have implemented IOC correctly, it ain't gonna make any difference.

RL89
  • 1,674
  • 4
  • 20
  • 35
2

I used to work with N-layered architecture for a while but in about a year ago our team decided to switch the focus on Onion architecture. To be honest, from the very beginning it was looking very complicated. We had exactly the same questions as everyone has like relations between/inside the layers, responsibilities and etc.

There are a ton of docs across the internet, however, if you use onion as much close to core principles as you can, then you feel much less pain at the end of the day.

Here are the core principles (rules) which we've decided to follow for:

  • core (also known as domain) should be used for any relations inside/between the layers
  • relations between services (also known as use cases) are allowed but input/output contract should be defined in the core (differentiate it from the other interfaces and use specific business logic interfaces)
  • infrastructure has no business logic and should be agnostic to an application used (e.g. if you have logger factory there, you should be able to copy it in different microservices w/o any adjustments have done)
  • data access has to be done through repositories and placed in the infrastructure layer. Nevertheless, repository is a kind of adapter between domain and data model.
  • Define data access interfaces in core in order to specify at least input params
  • onion absolutely independent from the way of programming. It works fine with classes and functions as well.
  • onion starts to work when your application is domain-based. Whenever you decouple the logic make it from the domain perspective neither from the functional ones
  • use the same directory structure in tests as you use in source
  • use the same layer names across the system. E.g. core layer should have exactly the same name everywhere (applicable for microservices)

The difference between N-layered architecture and Onion which I've noticed so far that you have a single place for contracts and it really helps you to follow Open/Close principle. And since the core layer shouldn't be changed very often (ideally shouldn't be changed at all) it helps you to pay attention to it during code-reviews.

I have created a simple scaffold project based on Onion. Have a look and feel free to ask if something unclear :)

https://github.com/YegorMedvedev/python-onion-scaffold

Yegor
  • 968
  • 1
  • 9
  • 13
1

To address your question directly "Isn't all of that achieved by merely adding façades to my traditional N-layered architecture?". The answer is yes, and no, depending on your use case.

The focus of the Onion architecture on using dependency inversion is as you said... "create looser coupling". However, it's not just for the sake of loser coupling. It might help to think of it as "protecting the parts of your code that are least likely to change, from parts that are more likely to change". So, for your case, would changes "below" the facade require changes to your "domain" code? Would a change to, say, a database object, trickle into changes to an object used in the facade and then into your "domain" code? If the answer to these types of questions is "no", then your assumption is correct, there's no meaningful functional difference for that code. If someone were to answer "maybe", then they may benefit from refactoring from facades to IOC.

Stuart H.
  • 46
  • 2
1

The main difference I have found in onion architecture and layered architecture is the location of the abstractions. When I think of layered architecture, the pattern I see a lot of is the interface and the implementation right next to eachother. So let's say you have a IPersonAccessor interface (I'm coming from C#) in MyApp.DAL/Personnel, then you would have a corresponding PersonAccessor class that implements the IPersonAccessor. That's all great as it allows you to switch out the implementation in the tests, but it doesn't really decouple it further than that.

Onion architecture (maybe this is only my own interpretation of onion architecture) allows us to decouple not only class from dependency but also layer from layer. Think about this scenareo, let's say you want to build a desktop version of your web project but using file storage rather than database storage. How would you do this with layered architecture?

In both cases, you would have to create a new version of your DAL. But how would you account for these two different layers in your business layer? In layered architecture, the abstractions are in the DAL, so in order for the business layer to compile, it has to include a reference to the layer in which the abstractions exist. So you can't just swap out the DAL because you need the one with the abstractions. And you can't just duplicate the interfaces in the new DAL for compiled languages because then (besides the duplication of code) just naming something the same doesn't make it the same to the compiler.

The solution is moving the abstractions to the layer that uses it. You would move the abstractions to the business layer. So in the example above, you would have MyApp.BL/Personnel/IPersonAccessor.cs and then you would have MyApp.DAL.DB/Personnel/PersonAccessor.cs as well as MyApp.DAL.FileStorage/Personnel/PersonAccessor.cs Each DAL classes would implement the IPersonAccessor and your IOC container (which most likely exists in your presentation layer) for your app could inject whichever PersonAccessor that it wants. This way, your DAL would reference/depend on the business layer so that it's implementations implement the abstractions which exist in the business layer. Now, the business layer can literally exist in complete isolation as far as dependencies are concerned. You can then swap out your DAL simply by injecting the implementations in the file storage DAL rather than the database DAL and the business layer can remain untouched since the classes of both DALs use the same exact interfaces... the ones that exist in the business layer.

With this new pattern, you get a weird, unfamiliar image. The DAL depends on the business layer. This is why the architectural pattern is thought of as an onion. The DAL is essentially another section of the outer layer. It exists on the same layer as the presentation layer. in fact, I think of this not so much as the DAL layer and the presentation layer... I think of it as the "infrastructure" layer. Inside the infrastructure layer, you have your data access code, your presentation code and other code that communicates with the outside world. Then, underneath this layer, protected from knowing the outside world even exists, is your business layer. It's an onion. one layer is protected from the outside world by another layer of multiple projects/assemblies giving it the likeness of an onion.

So, onion architecture is actually it's own architectural pattern because you have different layers and your interfaces are in a different place... or in other words, they are structured differently which is, by definition, an architectural pattern.

After pondering this architecture, I've started realizing that this really turns your business layer into more of a library that can be thrown in and out of applications since it has no dependencies other than maybe utility libraries like datetime libraries (like in python). This makes your application extremely dynamic because you can easily do things like change your platform, change to micro services, and then maybe change back to a monolith. Yes that's not a likely scenareo by any means, but for huge applications, this can be extremely convenient. All you have to do is re-write your infrastructure logic but your core business logic remains untouched.

Ian Kirkpatrick
  • 1,553
  • 7
  • 23