From the very beginning of our core product planning, we wanted to make the right decision about which principles and practices to follow to keep the application development process as simple as possible. At the same time, we wanted to be able to meet our complex business requirements.
Before we started planning the architecture of our software, we came across several issues that we needed to resolve. What we wanted to achieve before we started development was:
- A unified architectural principle that will support the future growth of the software complexity and software itself.
- Keeping up the clean code structure, business, and development rules that everyone can understand.
- A unified business language that everyone in the company (not only developers) will understand and that will be used in our business/product development process.
- The development process should not become more complicated.
We discussed many different approaches and patterns as well as best-practice examples in software development that could meet our demands. But in the end, none of them seemed to satisfy our needs better than the principle of Domain-Driven Design (DDD). So, we finally decided to work according to this principle.
What is Domain-Driven Design (DDD)?
Domain-Driven Design (DDD) stands for a unified approach to software design that provides a precise structure and set of rules that make software design decisions easier and more suitable for complex domains (business logic).
The key concepts of DDD are:
- Domain – A sphere of knowledge, influence, or activity. The subject area to which the user applies a program is the domain of the software.
- Model – A system of abstractions that describes selected aspects of a domain and can be used to solve problems related to that domain. It ensures the abstraction of the parts of the business logic into a suitable, descriptive and well-structured format.
- Ubiquitous language – Ensures that only a single language is used around business logic, both in business and product development.
- Context – This is basically a boundary where we remove any kind of ambiguity. It is a part of the software in which certain terms, definitions and rules apply consistently.
Ultimately, we can say that the focus of this approach is on real problem solving. The software is a tool for this.
Domain-Driven Design & Laravel – can it work?
You probably know about it, but just in case you don’t: Laravel is a free, open source PHP web framework created by Taylor Otwell and intended for the development of web applications following the Model-View-Controller (MVC) architecture pattern and based on Symfony.
At this point, however, we won’t go into Detail with Laravel, but we will talk about Domain-Driven Design and Laravel. For those who are not familiar with Laravel, I strongly recommend reading www.laravel.com.
There are several ways in which the Laravel framework can be organised to serve as a template for large-scale projects. But since we chose Domain-Driven Design, there weren’t that many proposed solutions on the internet at the time that gave us an idea of how this could be achieved or what the best structure for our purpose might be. Most of the time, the default Laravel project structure is simply sufficient to most of the use-cases which are just one of the things that makes this framework beautiful to work with.
Since we wanted to integrate DDD, we faced some challenges during this journey. The first of these hurdles was not to sacrifice the power of the features of the Laravel framework by introducing DDD and following it strictly. Even when there were some opinions that we should not introduce tightly coupling with framework features within our “domain”, we decided to adopt this rule and make an exception because we wanted to preserve the great Laravel features.
To be able to concentrate on one domain while keeping the framework features “intact”, we have developed the following code base structure:
- API directory
Represents the application itself as well as concepts, rules and business logic. The whole DDD focus is located in this layer. The structure there is:
All front-end code is located within this layer, which is responsible for managing the user interfaces. (In some of our next articles we will cover how we structure our frontend applications in Vue.js and how we created our own “style guide” that we use in all our products).
All of the application related playbooks which we use when we deploy our application via CI/CD pipeline.
With this code base structure, we were able to implement the DDD structure within our Laravel project, which will support the future growth of our software complexity and the software itself but will keep pace with the clean code structure.
Of course, domain-driven design is not just about code structure, it also involves some rules and requires that every developer understands the concept behind it in order to use it properly to implement new features. In addition, the implementation of DDD principles in our technology unit affected not only us, but the entire company: We had to work closely with our product owner and with colleagues from other units to define an appropriate ubiquitous language that everyone could understand and that we would express in our domain model, and later in our code. The creation of this ubiquitous language later not only helped us to create a consistent glossary of terms, but also facilitated the development of future features.
Conclusion: DDD is a steady process
In the end, my conclusion is that there is no 100% “right way” to create a DDD structure. Rather, it is a continuous process that must be carried out according to specific needs that can be adapted over time.
Our structure has changed a lot from its original state presented here and it will continue to develop in the upcoming period.
The idea behind this article was to give you a rough idea of how you can apply the DDD principles in your Laravel project. I hope that after reading it you will find the DDD principle interesting and become at least little bit familiar with it, and that you will come up with the same or even better structural ideas than we had at that point in time.