Lecture
When building an application, the easiest part is to create something that works. Building something performant, despite processing huge amounts of data, is a bit harder. But the biggest challenge is to create an application that can be maintained for many years (10, 20, 100 years).
Most of the companies I have worked for rebuilt their applications every 3-5 years, and some even every 2 years. This entails extremely high costs, has a huge impact on how successful the application is and, therefore, on how successful the company is, on top of the fact that it is extremely unpleasant for developers to work with a messy code base and drives them to leave the company. A serious company with a long-term vision cannot afford any of this, neither the financial losses, nor the loss of time, nor the loss of reputation, nor the loss of a customer, nor the loss of talent.
Reflecting the architecture and the domain in the code base is fundamental to the maintainability of the application and, therefore, is crucial to preventing all these unpleasant problems.
Clean Architecture is how I rationalize a set of principles and practices advocated by developers far more experienced than me, and how I organize the code base so that it reflects and communicates the architecture and domain of the project.
In my previous post, I talked about how I bring all these ideas together, and presented some infographics and UMLish diagrams to try to create a kind of conceptual map of how I think about it.
However, how do we actually apply this in practice in our code base?!
In this post, I will talk about how I reflect the architecture and domain of a project in the code, and propose a general structure that I believe can help us plan for maintainability.
In the last two posts of this series, I explained the two mental maps I use to help myself think about the code and organize the code base, at least in my head.
The first consists of a series of concentric layers that are ultimately sliced up to make up the application's domain modules, the components. In this diagram, the direction of dependency goes inward, which means that the outer layers know about the inner layers, but not the other way around.

The second is a set of horizontal layers, where the previous diagram is at the top, followed by the code shared between components (the shared kernel), followed by our own language extensions and, finally, the actual programming languages at the bottom. Here the direction of dependencies goes downward.

Using an architecturally-evident coding style means that our coding style (coding standards, naming conventions for classes, methods and variables, code structure, etc.) somehow conveys the domain and the architecture to whoever is reading the code. There are two main ideas about how to achieve an architecturally-evident coding style.
« […] An architecturally-evident coding style that lets you drop hints to code readers so that they can correctly infer the design. ”
George Fairbanks
The first is about using the names of code artifacts (classes, variables, modules, …) to convey both the domain and the architectural meaning. So, if we have a class that is a repository dealing with invoice entities, we should name it something like `InvoiceRepository`, which will tell us that it deals with the invoice domain concept and that its architectural role is that of a repository. This helps us know and understand where it should be located, as well as how and when to use it. That said, I think we don't need to do this with every code artifact in our code base; for example, I feel that post-fixing an entity with Entity is redundant and just adds noise.
« […] The code should reflect the architecture. In other words, if I look at the code, I should be able to clearly identify each of the components […] »
Simon Brown
The second is to make the subdomains explicit as top-level artifacts of our code base, as domain modules, as components.
So, the first should be clear, and I don't think it requires any further explanation. However, the second option is more complex, so let's dive into it.
In my first diagram, we saw that at the highest zoom level we have 3 different types of code:

So, at the root of our source folder, we can reflect these types of code by creating 3 folders, one for each type of code. These three folders will represent three namespaces, and later we can even create a test to make sure that both the user interface and the infrastructure know about the core, but not the other way around; in other words, we can check whether the direction of dependencies goes inward.
In an enterprise web application, there are usually several APIs, for example a REST API for clients, another one for webhooks used by third-party applications, perhaps a legacy SOAP API that still needs to be supported, or maybe a GraphQL API for a new mobile application…
For such applications, it is also common to have several command-line interface commands, used by Cron jobs or on-demand maintenance operations.
And, of course, it will have the website itself, used by regular users, but perhaps also another website used by the application's administrators.
These are all different representations of the same application; they all represent different user interfaces of the application.
Thus, our application can have several user interfaces, even if some of them are used only by non-human users (other third-party applications). Let's reflect this with folders / namespaces, to separate and isolate the several user interfaces.
Essentially, we have 3 types of user interfaces: APIs, CLIs and websites. So, let's start by making this distinction explicit inside the root UserInterface namespace, by creating one folder for each of them.

Then we go deeper into the namespace for each type and, if necessary, create a namespace for each user interface (for the CLI we may not need this).
Similar to the user interface, our application uses several tools (libraries and third-party applications), for example an ORM, a message queue or an SMS provider.
Moreover, each of these tools may require several implementations. For example, consider the case where a company expands into another country and, for pricing reasons, it is better to use a different SMS provider in each country: we will need different adapter implementations, using the same port, so that they can be used interchangeably. Another case is when we restructure the database schema or even switch the DB engine and need (or decide) to also switch to a different ORM: then we will have 2 ORM adapters plugged into our application.

So, in the infrastructure namespace, we start by creating a namespace for each type of tool (ORM, MessageQueue, SmsClient), and inside each of them we create a namespace for each of the provider adapters we use (Doctrine, Propel, MessageBird, Twilio, …).
In the core, at the highest zoom level, we have three types of code: components, shared kernel and ports. So, we create folders / namespaces for all of them.
In the Components namespace, we create a namespace for each component, and inside each of them we create a namespace for the application layer and a namespace for the domain layer. Inside the Application and Domain namespaces, we start by simply dumping all the classes in there, and as the number of classes grows we start grouping them as needed (I find it excessive zeal to create a folder just to put a single class in it, so I prefer to do this as needed).

At this stage, we need to decide whether to group them by subject (invoice, transaction, …) or by technical role (repository, service, value object, …), but I feel that, regardless of the choice, it doesn't really have a big impact, because we are at the leaves of the organizational tree, so if necessary it is easy to make changes to this last piece of the structure without much impact on the rest of the code base.
The Ports namespace will contain a namespace for each tool used by the core, just as we did for the infrastructure, where we will place the code that the core will use to make use of the underlying tool.

This code will also be used by the adapters, whose role is to translate between the port and the actual tool. In its simplest form, a port is just an interface, but in many cases it also requires value objects, DTOs, services, builders, query objects or even repositories.
In the Shared Kernel, we will place the code that is common to the Components. After experimenting with different internal structures for the shared kernel, I cannot settle on a structure that fits all scenarios. I believe that for some code it makes sense to split it by component, as we did in Core\Component (i.e. entity identifiers clearly belong to a single component), but in other cases not so much (i.e. events can be fired and listened to by several components, so they don't belong to anyone). Maybe a mix is a better fit.

Last but not least, we have our own language extensions. As explained in a previous post of this series, this is code that could be part of the language but, for some reason, is not. In the case of PHP, we can think, for example, of a DateTime class based on the class provided by PHP, but with some additional methods. Another example could be a UUID class which, although not provided by PHP, is by its nature very aseptic, domain-independent and, therefore, can be used by any project regardless of the domain.

This code is used as if it were provided by the language itself, so it must be entirely under our control. However, this does not mean that we cannot use third-party libraries. We can and should use them when it makes sense, but they must be wrapped by our own implementation (so that we can easily switch the underlying third-party library), which is the code that will be used directly in the application's code base. Ultimately, this can be a separate project in its own CVS repository and used across several projects.
All these ideas and the way we decide to put them into practice require a lot of attention, and they are not easy to master. Even if we manage to handle all of this, in the end we are only human, so we will make mistakes, and our colleagues will make mistakes, that's just how it is.
Just as we make mistakes in the code and have a test suite to prevent those mistakes from reaching production, we should do the same with the structure of the code base.
For this, in the PHP world, we have a small tool called Deptrac (but I am sure similar tools exist for other languages too), created by Sensiolabs. We configure it with a yaml file, where we define the layers we have and the allowed dependencies between them. Then we run it via the command line, which means we can easily run it in CI, just as we run the test suite in CI.
We can even generate a dependency diagram that will visually show us the dependencies, including those that violate the configured set of rules:

An application consists of a domain and a technical structure, the architecture. These are the real distinctions in an application, not the tools, libraries or delivery mechanisms used. If we want the application to be maintainable over the long term, both of these must be explicitly stated in the code base, so that developers can be aware of it, understand it, comply with it and, if necessary, refine it.
This clarity will help us understand the boundaries when writing code, which, in turn, will help us keep the application's design modular, with high cohesion and low coupling.
Again, most of these ideas and practices that I have talked about in my previous posts come from developers far more experienced and skilled than me. I have discussed them at length with many of my colleagues from various companies, I have experimented with them in enterprise application code bases, and they have worked very well on the projects I have been involved in.
That said, I believe that there are no silver bullets, no one-size-fits-all boot, no Holy Grail.
The ideas and structure that I mention in this post should be seen as a general template that can be used in most enterprise applications, but which should be adapted without regret when necessary. We always need to assess the context (project, team, business, …) and do our best, but I believe and hope that this template is a good starting point or, at least, food for thought.
Comments