1. The concept

  • DDD: Domain Drive Design
  • MVC: Model View Controller Model view control
  • OPP: Procedure Oriented Programming
  • AOP: Object Oriented Programming
  • OOP: Aspect Oriented Programming

DDD is just a methodology, an idea of software design. There is no stable technical framework. It’s not about technology, it’s not about storage, it’s not about communications. It’s the idea of absolute high cohesion and low coupling.

2. Contrast

Singleton vs. MVC vs. DDD domain model

2.1 Traditional MVC Architecture

  • Traditional development is based on the database as the core, from the bottom up, DB-> DAO -> service -> controller
  • With the continuous increase of business, poJOs will have more and more information. It is impossible to distinguish which business belongs to from the field alone. Business and data are separated and belong to an anaemic model, which will lead to anaemic amnesia.
  • In the picture below, the product list has basic information, prices and inventory, but you have no idea what the logic of the business is.
  • To the extent that it leads to the big mud problem, daOs and POJos are getting bigger and bigger, which is not conducive to the fragmentation of microservices. The DAO and DB are still together even after the Service is split. All fake splits.

2.2 DDD architecture

  • Top-down, backward-looking database design through domains.
  • In entity, the corresponding business behavior or method will be added, which belongs to the congestion model
  • In the figure below, entity description is used under the congestion model, and corresponding business methods will be added.

  • The application layer can access entities in different domains only through domain Services
  • If multiple information needs to be combined, the domain Service is used to combine and return the information
  • Domain first principle, the segmentation of different domains, different domains are isolated from each other, cannot communicate directly, need to access through MQ or interface, the actual development can use practice driven communication.
  • The domain layer is a sandbox where all domains are protected and accessed through domain Service calls.

Why domain-driven design?

There are many reasons to simplify domain-driven design for data storage. It’s fun to talk about why I want to promote domain-driven design in my company, because in the old data-driven development approach, which is the traditional multi-tier development architecture, they defined a bunch of Dales to manipulate data in. Net people generally have two ways to use, one is to use ORM like Entity Framework, the other want to use lightweight Mapping tool like Dapper, these are to transform relational data into objects. The result is the following.

Improper use of ORM results in excessive data loading and poor system performance. To solve the performance problem, it would be ugly not to load some navigation properties, but to return the DB Entity to the upper layer, so that some properties of the object are empty, and the upper layer can use this data without knowing when the property has value. So began to use some lightweight data methods, such as using Dapper and then write their own SQL statements, this is a very good way, but most people’s SQL ability is really dare not compliment, most of the SQL statements written, even worse than the statements generated by EnityFramework. , project to do so, I think we should be most of the processing business, how to make the programmer from the data store, model transformation of mire, domain driven design came into my line of sight, of course, the light from the data point of view is not enough to choose domain driven design, with a no database is solved? However, there are some problems with NoSQL, such as how MongoDB can make transactions and data consistency more elegant.

Many of the problems with our software, as we all know, are requirements, that is, customers’ requirements, which are difficult for us to understand accurately. As a result, programmers focus more on “HOW” rather than “WHAT”, and end up working on it for weeks or even longer, with customers saying, “WHAT? ! I told you”, but the customer told me, our understanding is different. For example, a client says, “Great job, I love you!” This Love is definitely not between a man and a woman. What we get is the demand of a customer. What is the context of his Love? If you’re playing basketball, you’re talking about basketball. If you’re playing table tennis, you’re talking about table tennis. In domain-driven design we can get business people more involved in the system, earlier in the system.

Ubiquitous Language Business people use the same Language as we do, and our programs to keep the business as domain-focused as possible, for example, in a traditional data-driven world, if Jack loves Rose, we would write something like this

Userservice. Love(Jack, Rose) but we business people very strange who Love who? Why UserService? If we write it like this

Jack.Love(Rose) and if we use

Company hire (employee) instead

Hire (company,employee) makes it easier to get business people involved, and the code can more easily represent real business scenarios.

4. Domain model

There are a lot of things in domain-driven design that we can apply to a variety of development patterns, so some of the things that follow can be used in part.

When it comes to field-driven fields, people will certainly start to talk about Bounded Context, aggregation and aggregation roots, which will easily confuse people. I think I’ll leave those concepts behind and talk about how to design aggregations, just to keep it simple.

In the past, we defined many models in the multi-level design, the database Entity Model, and then in order to not rely on the database, we designed the business Domain Model, and we designed the ViewModel, which is generally fine, and the responsibilities are very clear. But there are a few problems

We do a lot of model transformations, roll-in, roll-out. Of course, we can use AutoMapper, but the performance of AutoMapper is really difficult to flatter, you can search AutoMapper Performance on the Internet. The domain model becomes a pure DTO. Domain model First, we need to look at the domain, that is, we try to aggregate business into a domain, for example, we want to make a function, you can see the user’s login log every time, that login log actually belongs to the user’s domain.

Secondly, we look at the model. In the past, our models were all attribute only, that is, anemia model, anemia means no behavior, like a mummy. But in fact, the field is the most important place for us to accomplish business.

For example, if there is an Employee whose state is Active, Pending, or DeActive, the Pending state can only be changed to Active.

 public class Employee : Entity
{
    public Name Name { get; set; }
   
    public EmployeeStatus EmployeeStatus { get; set; }}Copy the code

If the Employee model is anaemic, we tend to code as follows

public class EmployeeService : IEmployeeService
{
    private readonly IUnitOfWorkFactory _unitOfWorkFactory;
    private readonly IEmployeeRepository _employeeRepository;

    public EmployeeService(IUnitOfWorkFactory unitOfWorkFactory, IEmployeeRepository employeeRepository)
    {
        _unitOfWorkFactory = unitOfWorkFactory;
        _employeeRepository = employeeRepository;
    }

    public void ChangeStatus(EmployeeStatus status, Guid employeeId)
    {
        using (var unitOfWork = _unitOfWorkFactory.GetCurrentUnitOfWork())
        {
            varemployee = _employeeRepository.GetById(employeeId); employee.EmployeeStatus = status; unitOfWork.Commit(); }}}Copy the code

However, the problem of the above code is that the domain is not autonomous. It is my responsibility to modify my state originally, so can you modify it? It is very dangerous to modify my state arbitrarily outside, for example, the Pending state can only be changed to Active state. So if it’s not an anemic model, we code like this and let the domain manage itself

public class Employee : Entity
{

    public UserId UserId { get; private set; }
    public EmployeeStatus EmployeeStatus { get; private set; }


    public void ChangeStatus(EmployeeStatus status)
    {
        if (this.EmployeeStatus == EmployeeStatus.Pending && status ! = EmployeeStatus.Active) {throw new Exception("Only can Active when status is pending");
        }

        this.EmployeeStatus = status; }}public class EmployeeService : IEmployeeService
{
    private readonly IUnitOfWorkFactory _unitOfWorkFactory;
    private readonly IEmployeeRepository _employeeRepository;

    public EmployeeService(IUnitOfWorkFactory unitOfWorkFactory, IEmployeeRepository employeeRepository)
    {
        _unitOfWorkFactory = unitOfWorkFactory;
        _employeeRepository = employeeRepository;
    }

    public void ChangeStatus(EmployeeStatus status, Guid employeeId)
    {
        using (var unitOfWork = _unitOfWorkFactory.GetCurrentUnitOfWork())
        {
            varemployee = _employeeRepository.GetById(employeeId); employee.ChangeStatus(status); unitOfWork.Commit(); }}}Copy the code

As you can see, we keep the business code in the domain as much as possible to make the domain autonomous.

Postscript In fact, the most difficult aspect of domain-driven design is the AggregateRoot aggregation, which will be discussed later, but I want to make the Domain not anaemic, so that this pattern can be used in traditional multi-layer design, data-driven architecture and so on.