Layered Architecture Is Good

Darek, the guy who reviews almost all Tidy Java posts before they are published, suggested that I could do a series on implementing different architectural styles, their pros, cons etc. I decided to give it a try and here comes the first one – Layered Architecture. To avoid misunderstandings, let me note that I will present the Layered Architecture as I was taught/learned, which might be different than the one that you know or some sources present. Enjoy!

What is Layered Architecture?

A Layered Architecture, as I understand it, is the organization of the project structure into four main categories: presentation, application, domain and infrastructure. Each of the layers contains objects related to the particular concern it represents.

  • Presentation layer contains all of the classes responsible for presenting the UI to the end-user or sending the response back to the client (in case we’re operating deep in the back-end).
  • Application layer contains all the logic that is required by the application to meet its functional requirements and, at the same time, is not a part of the domain rules. In most systems that I worked with, the application layer consisted of services orchestrating the domain objects to fulfill a use case scenario.
  • Domain layer represents the underlying domain, mostly consisting of domain entities and, in some cases, services. Business rules like invariants and algorithms should all stay in this layer.
  • Infrastructure layer (often aka persistence layer) contains all the classes responsible for doing the technical stuff like persisting the data in the database i.e. DAOs, Repositories or whatever else you’re using.

There are two important rules for a classical Layered Architecture to be correctly implemented:

  1. All the dependencies go in one direction, from presentation to infrastructure. (Well, handling persistence and domain are a bit tricky, because the infrastructure layer often saves domain objects directly, so it actually knows about the classes in the domain)
  2. No logic related to one layer’s concern should be placed in another layer e.g. no domain logic or database queries should be done in the UI.

The Essence of Layered Architecture

Architecture is kind of an overloaded term, so we should probably dig deeper into what the term really means in the context of layers. The main idea behind Layered Architecture is a separation of concerns – as we said already, we want to avoid mixing domain or database code with the UI stuff etc. The actual idea of separating a project into layers suggests that this separation of concerns should be achieved by source code organization. This means that apart from a guidance to what concerns we should separate, the Layered Architecture tells us nothing else about the design and implementation of the project. This implies that we should complement it with some other architectural processes e.g. some upfront design, daily design sessions or even full-blown Domain-Driven Design. Whichever we choose doesn’t matter, at least for the sake of layering, but we need to remember: Layered Architecture gives us nothing apart from a guideline on how to organize the source code.

Implementing Layered Architecture

Equipped with the knowledge of the layers to create, the relationships between them and the essence of the architecture, we are ready to implement it. As most of you probably expect, we will slice the system into layers by creating a separate package for each of them. When it comes to applying the dependency and separation rules, things are not so obvious. One could try putting each layer in a separate Maven module, but then capturing the weird relationship between domain and persistence would not be easy. I usually stick with packages and use common sense along with code reviews to make sure that none of the rules is broken.

Layering Spring Pet Clinic

Since I didn’t want to force you to learn a new project just to grasp a simple idea, I decided to use a project that all Java developers should be familiar with – Spring Pet Clinic. At the time of writing this post, the project uses some weird packaging, that is probably supposed to be more domain-oriented. Let’s see how the two ways of code organization stack against each other:

I’ll leave the interpretation to you. One thing that I did not expect and is now clear, is that there were no application services in the project – almost everything is done in the controllers! I haven’t seen it until I actually moved the classes to their best-fit layer packages.

Benefits of a Layered Architecture

Although some of you might still not believe, Layered Architecture has some benefits, including:

  • Simplicity – the concept is very easy to learn and visible in the project at first grasp
  • Consistent across different projects – the layers and so the overall code organization is pretty much the same in every layered project
  • Guaranteed separation of concerns – just the concerns that have a layer and to the point that you stick to the rules of Layered Architecture, but it’s very easy with the code organization it implies
  • Browsability from a technical perspective – when you want to change something in some/all objects of a given kind, they’re very easy to find and they’re kept all together

Drawbacks of a Layered Architecture

And, of course, Layered Architecture is not perfect. Some of its cons include:

  • No built-in scalability – when the project grows too much, you need to find a key to organizing it further by yourself. Principles of Layered Architecture won’t help you.
  • Hidden use cases – you can’t really say what a project is doing by simply looking at the code organization. You need to at least read the class names and, unfortunately, sometimes even the implementation.
  • Low cohesion – classes that contribute to common scenarios and business concepts are far from each other because the project is organized around separating technical concerns.
  • No dependency inversion – in a classical Layered Architecture the dependencies are direct and, conceptually, changes from a low-level infrastructure layer can propagate to more important higher layers.

When to Apply Layered Architecture?

At first, I wanted to write some cases when I’m using the Layered Architecture myself, but I think we could use a more methodical approach based on its pros and cons:

  • Simplicity That’s obviously important for everyone. The non-existent learning curve makes it especially viable for low experience teams and the ease of applying for projects with no budget to invest in a more advanced architecture.
  • Consistency – Multiple small projects handled by the same group of people e.g. as an internal architecture for microservices.
  • Separation of concerns – Low experience teams.
  • Technical browsability – To some extent it helps everyone. Depends on what you’re trying to find at a given moment.
  • No built-in scalability – Projects that are supposed to stay small i.e. microservices or bigger sized projects that can be easily factored into smaller pieces.
  • Hidden use cases & Low cohesion – Projects with no or little complex business scenarios e.g. CRUDs or simple REST services.
  • No dependency inversion – Projects with little dependencies on the infrastructure, in which there are no serious low-level changes to actually propagate higher.

Summary

As you can see, a Layered Architecture has its bright and dark sides. To me, it’s simplicity and consistency makes it a good fit for microservices without too much serious business logic. One could question if such microservices should exist in the first place, but realities of factoring big monoliths often make them a lesser evil. The most important lesson that you should take away from this article is: Layered Architecture is about organizing code for a good separation of concerns and nothing else, really.

About the Author Grzegorz Ziemoński

King of Tidy Java, nerd that thinks about producing perfect software all the time and proud owner of 2 cats.

follow me on: