Implementing Domain-Driven Design in Laravel
The process of software development is complicated. When we face problems, we usually try to tackle the complexity by turning it into more understandable and manageable pieces.
Domain-Driven Design is a software development methodology for tackling complex software projects to deliver an end-product that meets the goals of the organization. In fact, Domain-Driven Design promotes focusing the project on an evolving core model.
It will teach you how to effectively model the real world in your application and use OOP to encapsulate the business logic of the organization. This is how Martin Fowler [Biggest Software Artisan Lecture] describes it:
"In his excellent book Domain-Driven Design, Eric Evans creates a classification of the different kinds of domain objects that you're likely to run into."
In his book titled Domain-Driven Design, Eric Evans provides a combination of widely accepted best practices along with his own insights and experiences. If you are new to the idea of Domain-Driven Design, there is a lot to learn in this book.
What Is a Domain Model?
A Domain Model is your perception of the context in which it applies. Domain itself means the world of the business you are working with and the problems it wants to solve. For example, if you want to develop an app for online food delivery, your domain will be everything (problems, business rules, and so forth) about online food delivery that needs to be done in your project.
Model means your solutions to the problems of Domain. Exactly, in fact, Model is in the brain of the business expert. The domain is not a chart, UML, code or a diagram; Model is the Idea of how to draw diagrams. The Model should be focused on knowledge around a specific problem that is simplified and structured to provide a solution.
The Domain Model is your structured solution to the problem. The Domain Model should represent the vocabulary and key concepts of the problems of domain.
Ubiquitous Language is the language that is used by business experts to describe the Domain Model. It means that the development team uses the language consistently in all communications, and also in the code. This language should be based on the Domain Model. Here's the definition of ubiquitous language by Eric Evans:
"By using the model-based language pervasively and not being satisfied until it flows, we approach a model that is complete and comprehensible, made up of simple elements that combine to express complex ideas."
Let me give an example:
$product = new Entity/Product(); $product->setTitle( new Title('Mobile Phone')); $product->setPrice( new Price('1000')); $this->em->persist($product); $this->em->flush();
In the preceding code, I create a new product. But, in the application the product must be added, not created:
// add is a static method in product class $product = Product::add( new Title('Mobile Phone'), new Price('1000') );
In a development team, if someone creates a product and someone else adds a product, this will violate ubiquitous language. In this case, if in the product add method we have some extra actions such as sending email, they all will be missed, and also the definition of adding a product in the team will change. We would have two different definitions for one term.
In this article, I am not planning to talk about object-oriented design. Rather, Domain-Driven Design proposes the fundamentals of a good design. Eric Evans believes:
"Developing a good domain model is an art."
To develop a good domain model, you need to know about Model-Driven Design. The connection of the model and the implementation is called Model-Driven Design. The Layered Architecture is one of the Model-Driven Design blocks.
To tackle complexity, we need to separate concerns; in other words, we need to isolate every part of the design to concentrate more on specific sections. The system must be able to interact between sections. Layered Architecture is the idea of isolation of each part based on years of experience and convention. The Layers are listed below:
- User Interface
- Application Layer
- Domain Layer
- Infrastructure Layer
Let's describe each layer with details.
The User Interface is responsible for showing information to the user and interpreting the user's commands. In Laravel, the view is the User Interface (presentation) Layer.
The Application Layer is the way we communicate with the world outside (out of the application domain). This layer behaves as a public API for our application. It does not contain business rules or knowledge. Controllers in Laravel are located here.
The Domain Layer is the heart of business software. This layer is responsible for representing the concepts of the business, information about the business situation, and business rules.
The Infrastructure Layer provides generic technical capabilities that support the higher layers and also support the pattern of interactions between the four layers (that is why repositories are located in this layer).
Let me describe it a bit more. For example, if we are an e-mail company (such as Gmail), the e-mail would be in the model layer, but if we are a food delivery company, e-mail would be in our infrastructure layer. Why? Because sending e-mail for food delivery is not vital.
Connection between layers is mandatory, but without losing the benefit of the separation. The connection would be in one direction. As you see in the preceding schema, the upper layers can communicate with lower layers. If the lower layers need to connect with an upper layer, they must use patterns such as callbacks or observers.
Implementing Domain-Driven Design in Laravel
If you are inspired to see how Domain-Driven Design could be applied, I'll provide an example here using Laravel 5 as the underlying framework. I will describe the folder structure in this article and come up with other definitions in follow-up articles. To create the folder structure, a folder structure needs to be created in the app folder of the root directory, as shown below:
├── Presentation ├── Application ├── Domain ├── Infrastructure
Presentation is the view, where the blade files are put. Application is where the controllers are put. Laravel 5 lets the namespace of our controller be configured in App/Providers/RouteServiceProvider.php:
protected $namespace = 'App\Application\controllers';
You also can configure the path of the view in config/view.php:
// ... 'paths' => [ realpath(base_path('App/Presentation')), // ...
That is what it takes to configure the folder structure of Laravel. If you're wondering where Laravel's default model would be located, I should mention that a real repository in Domain-Driven Design must work with a simple PHP object as a database object, and Laravel Eloquent (Active Record) is a God object (big ball of mud). Also, to have good aggregates, you need to use Datamapper (Doctrine ORM) instead of Active Record.
In this article, I have presented you with the core concepts for Domain-Driven Design. However, I believe it may be hard to grasp it without additional information.
For example, Laravel needs extra packages and configuration to be fully adopted with Domain-Driven Design. I'll go deeper with this in the next article in this series on Domain-Driven Design.
About the Author
Alireza Rahmani Khalil is a Software Architect, Consultant, and Trainer of Enterprise Web Applications. For a significant chunk of his waking hours, he is a PHP expert, author, and speaker.