Can I Have Extra Onions With My Application Architecture Please?

We just released Zanshin 0.4.0 a few days ago. It’s been several years in the making and I learned new techniques and concepts during that journey. I tried to give talks at several Akademy along the way but they always gave more of a big picture view. Hence why I’m revisiting it this time focusing on only one aspect at a time.

I will start this series with the architectural pattern we settled on when we “rebooted” Zanshin. It will lead us to some of the other techniques we are using, but they will be really covered only in other installments.

The architectural pattern (or family of patterns in my opinion) is the “Onion”. For some reason I didn’t find any article on it using a summary structure close to the usual patterns presentation. So in case it is completely missing I will try to do that here. Let’s go.

Onion Architecture

Alternative names:

  • Hexagonal Architecture (or Ports and Adapters);
  • Clean Architecture.

Note that the Hexagonal Architecture could be considered to be slightly different from the Onion architecture as it deals mainly with only one of its aspects (the dependency to the infrastructure).

Intent

Separate concerns througout the application and avoid coupling to infrastructure.

Motivation

In any sufficiently complex application, it is common to see the application logic mixed with the GUI or to see the application logic tied to the data access. In the case of the KDE community it is obvious in the PIM space and I guess it would be true of other complex data oriented applications.

It is believed that those problems stem from the use of architectures based on the Layers pattern which tends to be very popular in business applications still. The problem of those layers is that they mandate the GUI depending on the application logic and the application logic depending on the data access. That creates a direct way for data access details escaping to the GUI slowly pushing you whole application to touch the storage API (e.g. KMail and Akonadi). Also, there is often the presence of an infrastructure layer where all the application dependencies are. It is then pushes even more non application specific details to the application logic like the data storage again or some piece of infrastructure tool as franca lingua (e.g. Kube and Sink intensive use of QAbstractItemModel). In both cases that ties your hands to a specific technology which might not be what you want.

It is a real problem which can kill projects. I’m not saying you can and should reinvent the wheel to have no dependency whatsoever (on the contrary), but surely you need a proper strategy to deal with them. Indeed, GUI technologies and data storage tend to change every couple of years. At the same time, they are not really essential to your application logic. That’s why, if you are aiming for the long maintainance game, you want those different concerns to evolve at their own pace as much as possible with the least impact on the whole application. Otherwise you end up piling technical debt until you give up and go for a rewrite loosing most of the application logic (since it will be rewritten). That means jeopardizing all the user value you created over the years.

The Pattern

“The main premise is that it controls coupling. The fundamental rule is that all code can depend on layers more central, but code cannot depend on layers further out from the core. In other words, all coupling is toward the center. This architecture is unashamedly biased toward object-oriented programming, and it puts objects before all others.”

“In the very center we see the Domain Model, which represents the state and behavior combination that models truth for the organization.”

Sidenote: Obviously combines well with the DDD approach, although it doesn’t mandate it.

Jeffrey Palermo, the first who coined the name, listed the four principles of the Onion Architecture which I list here verbatim:

  • The application is built around an independent object model;
  • Inner layers define interfaces, outer layers implement interfaces;
  • Direction of coupling is toward the center;
  • All application core code can be compiled and run separate from infrastructure.


{% mermaid %} graph LR; A–>B; A–>C; B–>D; C–>D; {% endmermaid %}

Design Decisions

what’s in the model. most strategic piece of design since it’s the most strategic to the application boundaries of the model what ends up in the different circles (although domain is always in the center and details are always on the outside) the amount of circles how the data flows and is modifited (might require another architectural pattern), in our case we went for a CRUD model was simple and good enough for us but a CQRS model could be an option for other systems

When to Use It

Of course, like anything else in this world and especially in our field, this architecture pattern is not a panacea. It is important to know when to use it or not so that the cure doesn’t turn into a poison.

Clearly seeing the amount of mechanisms you have to put in place and the heavy reliance on the dependency inversion principle, this is a pattern for applications with complex behaviors which will need to be maintained over a long period of time. Think your average “business” application, dealing with potentially (but not necessarily) large data sets and complex rules.

At the other end of the spectrum, it is likely not an architecture for small applications or heavily media oriented applications. For instance I doubt a simple music player or a painting application would benefit much from such a pattern.

See Also

Of course at this level of abstraction (architecture being really the 10000 feet view of your system), you have a lot of potential variants on the same idea. As such you might want to look at “the original” and some of the variants:

  • Alistair Cockburn’s Hexagonal Architecture which kind of started it all, it’s the oldest I could find in that family;
  • Jeffrey Palermo’s Onion Architecture which gave the name I use for this family of architectures;
  • Robert C. Martin’s Clean Architecture which is the most recent and in my opinion most refined instance of the idea.

In the case of Zanshin we’re somewhat close to something ressembling the Clean Architecture. It has been paying back quite a bit already. We’re doing more with roughly the same amount of lines of code. Also, the amount of tests code was multiplied by four and our coverage is around 95%. All of that with a much more manageable code base, it’s much easier to add or remove features. For instance, last year a few of my students who never saw the code base before managed to add a complete new view (the workday view) in a matter of weeks just coding on it here and there. Of course it came with all the necessary automated tests!

So definitely the Onion Architecture was the right choice for us, and it can be for you too if, as I highlighted above, you are in a similar type of application.