Layers, ports & adapters - Part 2, Layers

Posted on by Matthias Noback

The first key concept of what I think is a very simple, at the very least "clean" architecture, is the concept of a layer. A layer itself is actually nothing, if you think about it. It's simply determined by how it's used. Let's stay a bit philosophical, before we dive into some concrete architectural advice.

Qualities of layers

A layer in software serves the following (more or less abstract) purposes:

  • A layer helps protect what's beneath it. A layer acts like some kind of barrier: data passing it will be checked for basic structural issues before it gets passed along to a deeper layer. It will be transformed or translated to data that can be understood and processed by deeper layers. A layer also determines which data and behavior from a deeper layer will be allowed to be used in higher layers.
  • A layer comes with rules for which classes belong to it. If (as a team) you agree on which layers your software will have, you will know for every class you're wandering around with, in which layer to put it.
  • A system of layers can be used to modify the build order of a project. You could in fact build layer upon layer if you like. You can start at the outside, working inward, or at the inside, working towards the "world outside".
  • Being able to change the build order is an important tool for software architects. With layers you can build a big part of the application without deciding on which framework, ORM, database, messaging system, etc. to use.
  • Most legacy software consists of code without layers, which can be characterised as spaghetti code: everything can use or call anything else inside such an application. With a system of layers in place, and good rules for what they mean, and which things belong in which layer, you will have true separation of concerns. If you document the rules, and reinstate them in code reviews, I'm sure you will start producing code that is less likely to end up being considered "legacy code".
  • In order to do so, you need to write tests of course. Having a good system of layers in place will certainly make that easier. A different type of test will be suitable for each type of layer. The purpose of each test suddenly becomes more clear. The test suite as a whole will become much more stable, and it will run faster too.

A warning from Twitter:

I've never seen lasagna code to be honest, I did see a lot of spaghetti code. And I've written code that I thought was properly layered, but in hindsight, the layers were not well chosen. In this article I describe a better set of layers, for the biggest part based on how Vaughn Vernon describes them in his book "Implementing Domain-Driven Design" (see the reference below). Please note that layers are not specific to DDD though, although they do make way for a clean domain model, and at least, a proper amount of attention paid to it by the developer.

Directory layout & namespaces

Directly beneath my src/ directory I have a directory for every Bounded Context that I distinguish in my application. This directory is also the root of the namespace of the classes in each of these contexts.

Inside each Bounded Context directory I add three directories, one for every layer I'd like to distinguish:

  • Domain
  • Application
  • Infrastructure

I will briefly describe these layers now.

Layer 1 (core): Domain

The domain layer contains classes of any of the familiar DDD types/design patterns:

  • Entities
  • Value objects
  • Domain events
  • Repository interfaces
  • Domain services
  • Factories
  • ...

Within Domain I create a subdirectory Model, then within Model another directory for each of the aggregates that I model. An aggregate directory contains all the things related to that aggregate (value objects, domain events, a repository interface, etc.).

Domain model code is ethereal as I like to call it. It has no touching points with the real world. And if it were not for the tests, no one would call this code yet (it happens in the higher layers). Tests for domain model code can be purely unit tests, as all they do is execute code in memory. There is no need for domain model code to reach out to the world outside (like approaching the file system, making a network call, generate a random number or get the current time). This makes its tests very stable and fast.

Layer 2 (wrapping Domain): Application

The application layer contains classes called commands and command handlers. A command represents something that has to be done. It's a simple Data Transfer Object, containing only primitive type values and simple lists of those. There's always a command handler that knows how to process a particular command. Usually the command handler (which is also known as an application service) performs any orchestration needed. It uses the data from the command object to create an aggregate, or fetch one from the repository, and perform some action on it. It then often persists the aggregate.

Code in this layer could be unit tested, but having an application layer is also a good starting point for writing acceptance tests, as Gherkin scenarios (and run them with a tool like Behat). An interesting article to start with on this topic is Modelling by Example by Konstantin Kudryashov.

Layer 3 (wrapping Application): Infrastructure

Again, if it weren't for the tests, code in the application layer wouldn't be executed by anyone. Only when you add the infrastructure (or short "infra") layer, the application will become actually usable.

The infrastructure layer contains any code that is needed to expose the use cases to the world and make the application communicate with real users and external services. Think of anything that gives your domain model and your application services "hands and feet" and actually makes the use cases of your application "usable". This layer contains the code for:

  • Processing HTTP requests, producing a response for an incoming request
  • Making (HTTP) requests to other servers
  • Storing things in a database
  • Sending emails
  • Publishing messages
  • Getting the current timestamp
  • Generating random numbers

This kind of code requires integration testing (in the terminology of Freeman and Pryce). You test all the "real things": the real database, the real vendor code, the real external services involved. This allows you to verify all the assumptions your infrastructure code makes about things that are beyond your control.

Frameworks and libraries

Any framework and library that is related to "the world outside" (e.g. networking, file systems, time, randomness) will be used or called in the infrastructure layer. Of course, code in the domain and application layers need the functionality offered by ORMs, HTTP client libraries, etc. But they can only do so through more abstract dependencies. This gets dictated by the dependency rule.

The Dependency Rule

The dependency rule (based on the one posed by Robert C. Martin in The Clean Architecture) states that you should only depend on things that are in the same or in a deeper layer. That means, domain code can only depend on itself, application code can only depend on domain code and its own code, and infrastructure code can depend on anything. According to the dependency rule it's not allowed for domain code to depend on infrastructure code. This should already make sense, but the rule formalizes our intuitions here.

Obeying a rule blindly isn't a good idea. So why should you use the dependency rule? Well, it guarantees that you don't couple the code in the domain and application layer to something as "messy" as infrastructure code. When you apply the dependency rule, you can replace anything in the infrastructure layer without touching and/or breaking code in any of the deeper layers.

This style of decoupling has for a long time been known as the Dependency Inversion Principle - the "D" in SOLID, as formulated by Robert C. Martin: "Depend on abstractions, not on concretions." A practical implementation in most object-oriented programming languages implies defining an interface for the thing you want to depend on (which will be the abstraction), then provide a class implementing that interface. This class contains all the low-level details that you've stripped away from the interface, hence, it's the concretion this design principle talks about.

Extending "infrastructure" to everything that's needed to connect your application to users and external services, including code written by us or by any (hardware) vendor we rely on, we should humbly conclude that by far the biggest part of an application is concerned with simply connecting our tiny bit of custom (yet precious) domain and application layer code to the "world outside".

Architecture: deferring technological decisions

Applying the proposed set of layers as well as the dependency rule gives you a lot of options:

  • You can develop many use cases before making decisions like "which database am I going to use?". You can easily use different databases for different use cases as well.
  • You can even decide later on which (web) framework you're going to use. This prevents your application from becoming "a Symfony application" or "a Laravel project".
  • Frameworks and libraries will be put on a safe distance from domain and application layer code. This helps with upgrading to newer (major) versions of those frameworks and libraries. It also prevents you from having to rewrite the system if you ever like to use, say, Symfony 3 instead of Zend Framework 1.

This, to me, is a very attractive idea: I want to keep my options open, and I want to make the right technological decisions; not at the beginning of a project, but only when I know, based on what the use cases of my application are starting to look like, which solutions will be the best ones for the situation at hand.

Having seen a lot of legacy code in my career, I also believe that applying correct layering as well as enforcing the dependency rule helps prevent you from producing legacy code. At least, it helps you prevent making framework and library calls all over the code base. After all, replacing those calls with something more up-to-date, proves to be one of the biggest challenges of working with legacy code. If you have it all in one layer, and if you always apply the dependency inversion principle, it'll be much easier to do so.

Conclusion

As I mentioned in my previous post, with this nice set of layers, we know now that there is a time and place for your beloved framework too. It's not all over the place, but in a restricted zone called "the infrastructure layer". In fact, it's more like the domain and application layer are restricted zones, since the dependency rule has only consequences for these two layers.

Some may find that the proposed layer system results in "too many layers" (I don't know about 3 layers being too many, but anyway, if it hurts, maybe you shouldn't do it). If you want, you could leave out the application layer. You won't be able to write acceptance tests against the application layer anymore (they will be more like system tests, which tend to be slow and brittle). And you won't be able to expose the same use case to, say, a web UI and a web API without duplicating some code. But it should be doable.

At least, make sure that the biggest improvement of your application's design comes from the fact that you separate domain (or core) code from infrastructure code. Optimize it for your application's use cases, apply anything you've learned from the discipline of Domain-Driven Design, and bend ORMs and web frameworks to obey your will.

We still need to look at infrastructure code in more detail. This will bring us to the topic of hexagonal architecture, a.k.a. "ports & adapters", to be covered in another article.

Further reading

You may also check out Deptrac, a tool that helps to enforce rules about layers and dependencies.

PHP architecture design hexagonal architecture
Comments
This website uses MailComments: you can send your comments to this post by email. Read more about MailComments, including suggestions for writing your comments (in HTML or Markdown).
TobiMuc

Thank you very much for this interesting article. I have one question concerning the directory layout. Where do you usually put the (twig) templates? Do you separate them by bounded context (i. e. put them under src/Bounded Context/Infrastructure/…) or do you use one global location like src/templates?

Sebastiaan Stok

Usually I put them in Infrastructure as it heavily depends on what template engine you use (Twig, Php itself, Blade), and I tried to heavily split them per user interface (web, email, files (invoices, orders)) but figured this was to much work without any actual gain.

Now I simple keep them in "Infrastructure/Resources/templates" with sub-directories for emails, and files. The web related templates are split per section (client, admin, and shared).

Matthias Noback

Thanks. I don't think it's too important to separate these template files by context, if it's a lot of work. You could have them all in one place.
Op wo 16 jan. 2019 15:40 schreef Disqus

systemovich

Does anybody know of a real-world open source application that implements this methodology?

Mark van den Berg

First I want to say how much I enjoy and respect your clear writing on so many interesting topics. I've now read most of your posts in the last year or 2, but I'd love to see you expand a bit on the role of the Application layer, and particularly how you distinguish it from the Domain layer.

In a traditional Presentation/Application/Domain/Infrastructure layering, I would consider the Application layer to contain those objects that a) are not specific to any external interface (UI, API, console, etc.), b) do not belong in the (all-in-memory) domain, but c) have a dependency on the domain. So this includes things like authentication/authorization, repository implementations, Doctrine event subscribers and mapping types, etc. I understand that, with hexagonal architecture, many of these things would go into the outer Infrastructure layer, as persistence should also be considered an external interface. So what does the Application layer do, as you see it?

From the brief section in this blog post, I gather that you use the Application layer primarily (only?) to provide entry points into the Domain layer (use cases) to the Infrastructure layer. Does that not make the command handlers just a type of domain services?

Mark van den Berg

I've realised that I may have had a bloated idea of Domain. Handling simple CRUD-type commands (e.g. creating a user, updating an address) does not go into a domain service. Domain services encapsulate domain knowledge. Application services may call on domain services to impose business constraints and apply business logic, but the overall command handling itself (fetching relevant entities, orchestrating changes, persisting etc.) belongs in the application layer.

Matthias Noback

Also, thanks for the kind words!

Matthias Noback

Thanks for commenting here; application services aren't often discussed very well, and the name isn't very good too, because, what wouldn't be part of the "application", right?
Anyway, I think you already get it. If you want to read more, there's an entire chapter in "Implementing Domain-Driven Design" on the topic. Basically, application services will do whatever is part of a certain use case. This involves reading from (abstract) repositories, saving to (abstract) repositories, and invoking behavior on domain objects (i.e. aggregates, domain services).

It comes with two nice options: you can test against the application layer, showing that a use case "works", without considering complicated and slow things like the UI or a database. Furthermore, it allows replacing or upgrading these infrastructure parts when the time is ready.

Rasta

Hi, thank you very much for this post. I have one question regarding the statement about "commands belonging to the application layer". There is a discussion under this post http://ocramius.github.io/b... where Matthias Verraes and Marco Pivetta are stating, that commands should be defined in the domain model (so basically in the domain layer).

To be more precise, Matthias Verraes writes:
"Commands are domain objects, repositories are domain objects, aggregates are domain objects, and yet for some reason everybody keeps insisting that command handlers, which glue it all together, are somehow elsewhere in the lasagna."

and

"Commands are part of the _domain model_. A domain model is a set of abstractions of concepts from the domain, that we choose to solve problems for that domain, in the language of the domain. That set of abstractions can include measurements and states (represented in code as VOs), things or identifiable concepts (represented as entities), collections of things (eg a fleet is a domain concept representing a collection of cars we own, so fleet can be a repository), behaviours or processes or lifecycles involving changes to those things (which we can describe as instructions (commands) and outcomes or facts (events)), and questions about the state of things (queries). I don't see any problem here. Traditionally people have overfocused on modelling entities exclusively, but that view is slowly shifting.

If I send you an invoice, that invoice is a message with the specific intent of instructing you to pay me: that is a command which clearly originated in the domain. If I instruct my accounting department to send you an invoice, that's also a command. If I automate my accounting department, and instruct my software system to send you an invoice, I see no difference."

and Marco writes:

"Commands are 100% domain in my opinion (feel free to discuss): they are
part of the ubiquitous language, and that intent is indeed discussed
with business."

As for my opinion, considering that Matthias also states (elsewhere) that a process manager is a domain service, commands should be part of the domain model, because process manager needs to instantiate them. But still the problem about where to place the commands has these two opinions and I would like to get a final statement to be perfectly sure.

Matthias, what would be your opinion in this discussion? Thank you

Matthias Noback

As long as use use ports/adapters and don't mix up infrastructure code in any of these things :)
Personally, yes, I think that entities, values objects, domain events, repository interfaces and possibly domain services (but it's a confusing concept) belong to the Domain layer. Everything that describes uses cases (i.e. application services/command handlers and commands), or coordination between aggregates based on these domain events (e.g. event listeners, which includes sagas/process managers), but also read models and their repositories, which serve specific use cases, belongs to the Application layer.

dessasandoval

matthiasnoback yes

Bert Ramakers

Follow-up question: It's not specifically mentioned where projectors / read models / query objects belong in the directory structure. Are these also part of the application layer since they help to communication with the domain layer like commands and command handlers do?

Matthias Noback

This is a bit of a fuzzy area. Since read models support the use cases of an application, they often belong to the application layer. If the projector is decoupled from its underlying infrastructure, it can be in the application layer too, but if not, in the infrastructure layer (while the model/query object itself can still remain in the application layer, if these aren't coupled to the underlying infrastructure). I allow myself to make an exception for methods like Query::fromRequest(Request $request) by the way. Strictly speaking this makes the class an infrastructure class, but I keep it in the application layer.Sometimes I want an aggregate in context A to work a domain model that in its "full form" belongs to context B. Then I add a local model to context A, whose state comes from context B. This is more like an immutable aggregate then (it's immutable because its state will be managed in another context).

Bert Ramakers

Interesting article. I was recently discussing it with a co-worker, and we were wondering if you would put sagas and process managers in either the application layer, or the domain layer? The way I figured, a saga or process manager often contains domain logic so it should be inside the domain layer. But they also dispatch commands, which would make them (and in turn the domain layer) depend on the application layer. What are your thoughts on this?

Matthias Noback

Good question. Yes, sagas/process managers belong to the application layer, since they coordinate interactions spanning multiple aggregates, and often multiple contexts. They use commands and events, which are all part of the application layer.

Rasta

Isn't the part about Sagas coordinating multiple contexts just another part of the domain? Some kind of a coordinating domain, or something... Just an idea... There are people stating that Sagas/PMs are domain services.

Bert Ramakers

Good point about them often spanning multiple bounded contexts.

"They use commands and events, which are all part of the application layer."

> Aren't the events part of the domain layer though? (Not that that prevents the sagas / process managers from being part of the application layer.)

Matthias Noback

Yes, they usually are, but you can "convert" any domain event (originating from an aggregate) into some higher-level event, or otherwise use it to translate domain-level events to events across contexts.

I have a doub. You say "Domain model code is ethereal as I like to call it. It has no touching points with the real world."but I have often read about the concept of ubiquitous languag that tell us to talk in the code like in real life so the domain expert can be a usefull player to the team. What do you tink about that?

Matthias Noback

I agree about that. The modelled concepts should certainly be related to the real business you're modelling for. I meant that the objects from the domain layer don't reach out to any infrastructural ("real world") thing - e.g. a database, an external service, a UI, etc.

Douwe de vries

A good article thank you for sharing.
I have a question about your directory layout.
I’ve read numerous articles about how to structure code. I.e. where to place things.

I have had projects where i had a similar structure as yours, but one article showed me an alternative.
It said that organising your code should reflect your domain.
It said don’t group by technical concept but by domain concept.
It did have the folders for the layers though.

What are your thoughts on this approach?

i’m really not sure which one i personally prefer….

Matthias Noback

Hey Douwe, sorry for not responding, didn't see this before. I agree with the advice given: I don't like directory structures that keep all entities, all event listeners, all controllers (i.e. all technical things) together, but separated. So I like the division by domain or use case. What's missing in the discussion above is mainly the name of the "bounded context". This is where Vaughn Vernon in the book Implementing Domain-Driven Design proposes a root namespace for the bounded context name, and then within every BC a subnamespace/subdirectory for every layer. I find this works very well. See also the next article for a few more pointers.

Miha Vrhovnik

I'm learning this as I go and still have some trouble. e.g. Let's take for example the User, there is a password field in there that must be somehow hashed. So in the inner layer there is UserModel, then there comes the Command and Handler for user registration and then there are two ways of registering user via command line and via http form.

I would put the hasher function into the handler, the actual dependecy implementing that would be injected via constructor.

Now lets say that it has been decided that the Symfony will be used as our framework of choice (And let's be honest nobody really changes framework when the app is written). The Symfony already provides the PasswordEncoderInterface, but as this is infrastructure, then I should create another PasswordEncoderInterface inside Application layer. And then inside infrastructure I should implement an class implementing the interface from Application layer which gets injected the instance of class from Symfony.

Aren't we complicating our life a bit to much?

Matthias Noback

Not all third-party code is necessarily infrastructure. You can adopt some third-party code in your deeper layers. This means you could adopt the external interface as well. However, these interfaces aren't often exactly what you need. Also, if your project is a serious project that wants to survive for several years, I really think you should consider keeping this kind of framework code out of your core. As I know from experience, people will switch frameworks (in just a few years).

Stefan

Very helpful article series, thanks for sharing.

If I got it right, you use more of Symfony than some time ago. Do you use bundles again? I'm wondering what the best way is to combine the bounded contexts and the layers with bundles without giving up that strict schema.

Code-wise it seems clear: Build light-weight abstractions to encapsulate e.g. the event dispatcher to be able to use framework functionality in the application and domain layers.

What about resources like translations and views? My guess would be to either add them to app/Resources to be able to abstain from bundles at all. Or use the bundle best practices to put the files at the "default" locations. In either way, resources are separated from their contexts, which doesn't look that desirable. But they need to be addressable by Symfony, so can't lie in the layer structure. Very unsure about a good solution.

Pedro Fornaza

What i like to do is build the domain and application layers agnostic, outside a bundle. And encapsulate the infrastructure on a bundle, since its the "glue code". You can still put your resources on the bundle, they are framework specific.

I dont think you need to encapsulate the event dispatcher to use on domain. In my view, domain and application events are very different things. Depending on your needs and implementation, you dont even need a dispatcher to domain events.

Matthias Noback

Thanks for letting me know!

Last time I checked, lots of "Symfony things" could be moved to a different location. It may take a bit of extra effort, but I would love to have all of it in Infrastructure. If that means I have to reintroduce the bundle as a convenient "root" for all my resources, that's no problem to me. Full disclosure: I haven't been doing any of my old "naked bundle" stuff, or basically any other Symfony full stack development lately. Mostly I've been using more light-weight frameworks like Zend Expressive, but then again, I haven't done big projects for a while.

Great article.

I have things slightly differently:
I have two layers: a domain layer, which is driven from examples (using the modelling by example technique you mentioned, and an application/infrastructure layer. I don't make a deliberate distinction between application and infrastructure.

I start by focusing on the domain itself as the core business logic, which can be moved to another code base, reused in a different framework, etc. This is covered, developed and driven by the scenarios/examples describing the application business logic (MbE). The domain is run using in-memory repositories. The classes in the domain have unit tests.

Then I code the outer layer (app/infra) using the exact same scenarios to test it from one end to another.

Matthias Noback

Thank you.
Also thanks for explaining your own approach! It could take away some of the overhead of maintaining a separate application layer.

Dmitriy Lezhnev

Mathias, great stuff on clean architecture! Are you planning on making a sample app to show how your ideas work in the metal?

Matthias Noback

This has been for long my wish. Not currently on the planning though.