Domain-Driven Design and Clean Architecture in PHP — Part 1 (Layers and Structure)

Andrew Pogulailo
4 min readJun 16, 2022

--

Programmers laptop
Photo by Arnold Francisca

This will be a series of articles on Clean Architecture and Domain-Driven Design in PHP. It will be difficult next, but I tried to take the best of these two practices. As a result, we get a powerful tool based on which you can develop a system of any complexity.

Example project

Let’s take for example a simple project, the Editor of Articles for an online newspaper.

Since our newspaper cares about the accuracy of the information, we must follow what is on our pages. Therefore, it was decided that all articles must be checked by the editor-in-chief. That is, each author can create an article, but it can be published only by the editor-in-chief, who will check the text for accuracy and only then publish it. The newspaper also wants the most interesting articles to be read-only by the premium category of readers who have subscribed for a fee.

Therefore, we can pre-identify three bounded contexts: Authorization, Payments, and Editor. In the next part, we will focus in more detail on the topic of analysis using the Event Storming technique. Perhaps after a more detailed analysis of the bounded contexts will become more.

Dependency rule

The main and common rule for Clean Architecture and Domain-Driven Design is the dependency rule.

1. Adapter layer
2. Application layer
3. Domain layer

Nothing in a lower layer can know anything at all about something in a higher layer.

In other words, the code on the lower layers should not depend on the code on the higher layers. The code at the higher levels should depend only on the code at one level below.

By using this rule, we make our code independent of frameworks, databases, external services, and external libraries. Therefore, in the future we can, relatively easily, change the framework for example from Laravel to Symfony. Change your service provider if your business needs it for some reason. We will no longer need to look for dependencies throughout the application, it will be enough to rewrite the code of only one layer.

This is implemented using the Dependency Inversion approach. If we need to connect a third-party library for use on the Application layer, we create an easy-to-use interface on this layer, and on the Adapters layer, we implement this interface and adapt the third-party library to our interface instead of using the library directly. We will talk about this in more detail in the next parts.

Directories structure

I believe that the layer structure is closely related to the directory structure, so I will look at layers and directories together. On the Internet, almost all articles on this topic do not consider the structure of directories at all, so this can be a very difficult topic for beginners. I will try to fix it.

Let’s deal with the structure at the high level, and gradually delve into the lower levels. Our bounded contexts will be implemented as separate modules. Each of them is divided into three layers, in fact, four of them, but the Infrastructure layer is everything that is outside our modules (web server, framework, database, etc.). So we’ll look at three layers, but remember that there are four.

.
└── src
├── Authorization
│ ├── Adapter
│ ├── Application
│ └── Domain
├── Editor
│ ├── Adapter
│ ├── Application
│ └── Domain
└── Payments
├── Adapter
├── Application
└── Domain

Domain layer

The code on this layer is the core of your domain (business), here are rich domain models that fully describe the business rules. In our case, these are the aggregates, entities, and value objects of your application built according to Domain-Driven Design practice. There are also factories responsible for creating entities.

This code changes the least, and does not depend on other layers, the code of this layer may have no dependencies other than the code placed on this layer. Also, if necessary, the code of this layer can be reused in other applications related to this domain.

.
└── Domain
├── Entity
└── Factory

Application layer

This layer contains code specific to this application. This layer is responsible for all ways of using our entities. Here we describe exactly how we can interact with our entities.

The code on this layer can only depend on the code on the domain layer. Also in this layer, we describe the interfaces for our repositories through which we will get our entities and the services we need but do not implement.

.
└── Application
├── Interactors
├── Repository
└── Service

Adapters layer

Here lives the code that connects or “adapts” the code located on the Application layer with the Infrastructure layer (framework, database, external services, external libraries, etc.).

This layer will have the least code. Here we write the implementation of our interfaces, and the code is not related to the business logic of the application. For example, Authentication will be on this layer, because it does not usually apply to business logic, unlike Authorization.

.
└── Adapter
├── Controller
├── Repository
└── Service

Сonclusion

This was the first in a series of articles on Clean Architecture and Domain-Driven Design in PHP. In this article, we have become acquainted with the structure of layers and directories. In the next article, we will look at the topic of application analysis using the Event Storming technique. And finally, we will start writing the code, and get acquainted in more detail. I hope you are interested in this topic.

--

--

Andrew Pogulailo

I specialize in developing high-load systems. I'm passionate about using my skills to create innovative web solutions that drive business success.