[JAVA] DDD personal notes

Introduction

--Read the DDD book and leave a note of your understanding (updated from time to time). ――For verifiability, I try to quote my opinions as much as possible. ――I think that the implementation example is decided from the Java language specification separately from the concept of DDD. --The code example assumes a Todo app like Wunderlist.

Miscellaneous feelings on the way

――When you read the explanation of domain model and domain service, I strongly feel that familiarity with business rules leads to problem isolation. --Central processing (domain model, repository, factory) as a concept that a facility called a bank should have ――You can't withdraw more than your account balance ――What is the interest rate of investment products? --Domain service ――Transfer processing, reassembling processing, etc. --Application service to increase the productivity of bankers ――You can exchange in yen or Japanese, or you can get the form in XML. --Infra service of general system ――You will be notified by email that the transfer has been completed.

Hmmm, I feel that the Todo app has a bad example. In the case of Todo, there is no choice but to enter or delete it, and there is almost no calculation. The only calculation that a collection object should have is to sort DueDate in ascending order, collect Favorites (true) and line them up in the front, and the rest in the back. You can calculate the digestibility of Task. There is no responsibility to calculate something with one Todo.

layer

+-- domain
|    |
|    +-- model
|    |    |
|    |    +-- Entity -- Aggregate
|    |    |
|    |    `-- Value Object
|    |
|    +-- Factory
|    |
|    +-- Repository ( command , query )
|    |
|    +-- Service
|    |
|    `-- ( shared )
|
+-- infrastructure - service
|    |                |
|    |                `-- ( mail sender )
|    |
|    `-- ( Repository Implementation )
|         |
|         +-- ( db I/O )
|         |
|         +-- ( file I/O )
|         |
|         `-- ( cache I/O )
|
+-- application -- Service
|
`-- interfaces
     |
     +-- ( controller )
     |    |
     |    `-- ( REST )
     |          |
     |          +-- ( XML )
     |          |
     |          `-- ( JSON )
     |
     +-- ( view )
     |    |
     |    `-- ( HTML )
     |
     `-- ( socket )


domain model

Reference books:

--Concept / Explanation -[Eric Evans Domain Driven Design (Evans)](https://www.amazon.co.jp/%E3%82%A8%E3%83%AA%E3%83%83%E3%82%AF % E3% 83% BB% E3% 82% A8% E3% 83% B4% E3% 82% A1% E3% 83% B3% E3% 82% B9% E3% 81% AE% E3% 83% 89% E3 % 83% A1% E3% 82% A4% E3% 83% B3% E9% A7% 86% E5% 8B% 95% E8% A8% AD% E8% A8% 88-Architects% E2% 80% 99 Archive-% E3% 82% BD% E3% 83% 95% E3% 83% 88% E3% 82% A6% E3% 82% A7% E3% 82% A2% E9% 96% 8B% E7% 99% BA% E3% 81% AE% E5% AE% 9F% E8% B7% B5-% E3% 82% A8% E3% 83% AA% E3% 83% 83% E3% 82% AF% E3% 83% BB% E3% 82 % A8% E3% 83% B4% E3% 82% A1% E3% 83% B3% E3% 82% B9 / dp / 4798121967) -[Practical Domain Driven Design (Vernon)](https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5%E3%83%89%E3%83%A1%E3% 82% A4% E3% 83% B3% E9% A7% 86% E5% 8B% 95% E8% A8% AD% E8% A8% 88-% E3% 83% B4% E3% 82% A1% E3% 83 % BC% E3% 83% B3% E3% 83% BB% E3% 83% B4% E3% 82% A1% E3% 83% BC% E3% 83% 8E% E3% 83% B3-ebook / dp / B00UX9VJGW ) --Implementation example -Principles of system design useful in the field (Masuda) -Kato Jun's Technical Diary DDD (Kato)

Entity

Entity overview



//Variable attributes other than identifiers
//Because the attributes are independent of each other in this class@Setter is specified
//When a relationship is created between attributes, perform attribute operations via the business logic of this class.
@Getter
@Setter
public final class Todo extends AbstractEntity<Todo> implements ValueObject {

    // (AbstractEntity::private final UUID identifier)
    private Title title;
    private DueDate dueDate;
    private TaskList taskList;
    private Memo memo;
    private Favorite favorite;

    public Todo(final EntityIdentifier<Todo> identifier, final Title title, final DueDate dueDate, final TaskList taskList, final Memo memo, final Favorite favorite) {
        super(identifier);
        Validate.notNull(title);
        Validate.notNull(dueDate);
        Validate.notNull(taskList);
        Validate.notNull(memo);
        Validate.notNull(favorite);

        this.title = title;
        this.dueDate = dueDate;
        this.taskList = taskList;
        this.memo = memo;
        this.favorite = favorite;
    }

    @Override
    public Todo clone() {
        return super.clone();
    }

    @Override
    public boolean equals(final Object that) {
        if (this == that) {
            return true;
        }
        if (that == null || getClass() != that.getClass()) {
            return false;
        }
        Todo o = (Todo) that;
        if(!entityIdentifier.equals(o.entityIdentifier)){
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = Objects.hashCode(entityIdentifier);
        result = 31 * result;
        return result;
    }
}

-** I want to track an object using identity (= have an invariant identifier throughout the life cycle) ** --What is Entity identity? --Different from the identity compared by the Java == operator -* This identity mechanism makes little sense in other application domains. (Evans No.2348-2349) * --It is a mechanism to realize the identity on the memory, and if it goes out of the memory, the identity will be lost. -* Identity is a clever and meaningful attribute of an entity and cannot be inherited by the automated functionality of a programming language. (Evans No.2349-2350) * -* Continuity throughout the life cycle, where important distinctions for application users are made independently of attributes (Evans No.2339-2340) * --Identity = identity-based equivalence, equivalence = value equality -* It is not an entity that is usually used to mean an entity. (Evans No.2339) * --In some cases, it may be Entity or ValueObject ――There is no delimiter in the concept of words, it is decided by how they are treated in the model -* The mail order company's software requires an address to confirm the credit card and address the parcel. However, even if the housemates order from the same company, it is not important to realize that they both live in the same place. In this case, the address is a value object. (Evans No.2500-2502) * -* Postal service software has a hierarchical format consisting of provinces, cities, postal districts, and blocks to systematize delivery routes, which extends to individual addresses. These address objects derive the zip code from the parent in the hierarchy, and if the postal service decides to reassign the postal code, all the addresses in it will move together. Here, the address is an entity. (Evans No.2503-2506 * -** Identity is guaranteed by making the identifier invariant. Therefore, the attributes are rewritten ** -* If an object is identified by identity rather than attribute, then that identity should be the first priority when defining this object in the model. (Evans No.2363-2364) *

Value Object

Value Object overview



// ----Basically this--------------------------

//Attributes are immutable
@Getter
public final class Task implements ValueObject {
    private final String value;
    private final Boolean done;

    public Task(final String value) {
        Validate.notNull(value);

        this.value = value;
        this.done = false;
    }

    public Task(final String value, final Boolean done) {
        Validate.notNull(value);
        Validate.notNull(done);

        this.value = value;
        this.done = done;
    }

    @Override
    public boolean equals(final Object that) {
        if (this == that) {
            return true;
        }
        if (that == null || getClass() != that.getClass()) {
            return false;
        }
        Task o = (Task) that;
        if (!value.equals(o.value)) {
            return false;
        }
        if (!done.equals(o.done)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = Objects.hashCode(value);
        result = 31 * result + Objects.hashCode(done);
        return result;
    }
}



// ----Have a Value Object in the child--------------------------

//Attributes are immutable
//However, if the attribute is an object such as List, some ingenuity is required.
public final class TaskList implements ValueObjectList<TaskList, Task> {
    private final List<Task> taskList;

    public TaskList() {
        taskList = Collections.unmodifiableList(new ArrayList<>());
    }

    public TaskList(final List<Task> list) {
        Validate.notNull(list);

        // unmodifiableList()The reason is
        //List returned by passing by reference<Task>Make sure that the contents cannot be rewritten
        //If you want to rewrite it, you want to force the use of the constructor.
        //Since the final declaration does not work even in the element, it is used in this position even in the declarative sense.
        taskList = Collections.unmodifiableList(list);
    }

/*
  Collections.unmodifiableList()The reason for using
If you do not use it, the following test code will not pass and you cannot guarantee that the attributes will not change.

When using taskList.set(0, after)UnsupportedOperationException occurs in
If you want to rewrite the element, ArrayList<>(taskList)By passing to the constructor of TaskList
While rewriting the element, the attribute is immutable (= another object with different attribute because it uses the constructor) can be guaranteed.

        TaskList target = new TaskList();

        Task before = new Task("Buy milk", false);
        target.add(before);
        assertEquals(1, target.getList().size());

        //Overwrite the first of the retrieved list with another object
        List<Task> taskList = target.getList();
        Task after = new Task("Sell milk", true);
        taskList.set(0, after);

        //The overwritten amount is not reflected in the variable before secured before overwriting.
       assertEquals(before.getFavorite(), target.getList().get(0).getFavorite());
 */

    private static void accept(final Task w) {
    }

    public Optional<Task> find(final Task searchTask) {
        Validate.notNull(searchTask);

        Optional<Task> ret = Optional.empty();

        for (Task existTask : getList()) {
            if (existTask.equals(searchTask)) {
                ret = Optional.of(existTask);
                break;
            }
        }

        return ret;
    }

    public List<Task> getList() {
        return taskList;
    }

    public TaskList add(final Task... t) {
        Arrays.stream(t).forEach(Validate::notNull);

        List<Task> result = new ArrayList<>(taskList);
        Arrays.stream(t).forEach(
                v -> find(v).ifPresentOrElse(
                        TaskList::accept,
                        () -> result.add(v)
                )
        );

        return new TaskList(result);
    }

/*
The reason for regenerating and returning myself is
Internal attribute is Collections#unmodifiableList()It cannot be changed because it has not changed in.

As a caveat, List#add(Object):If you use it as a boolean, this will not be updated and you will not receive a new value.
*/

    public TaskList remove(final Task... t) {
        Arrays.stream(t).forEach(Validate::notNull);

        List<Task> result = new ArrayList<>(taskList);
        Arrays.stream(t).forEach(
                v -> find(v).ifPresent(result::remove)
        );

        return new TaskList(result);
    }

    @Override
    public boolean equals(final Object that) {
        if (this == that) {
            return true;
        }
        if (that == null || getClass() != that.getClass()) {
            return false;
        }
        TaskList o = (TaskList) that;
        if (!taskList.equals(o.taskList)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = Objects.hashCode(taskList);
        result = 31 * result;
        return result;
    }
}

-** In principle, attributes and their contents are not rewritten. Should be disposable. ** ** --For two people with the same surname and the same name in the customer data, in Value Object, if they have the same surname and the same name, one object may be shared by two people. However,, -* If one person's name is changed, the other person's name may also change! To prevent this and to share objects safely, the objects must be immutable. This means that you can't change it except by completely replacing it. (Evans No.2533-2535) * --In some cases, it may be variable. -* When the value is updated frequently, when the cost of creating and destroying objects is high, when there is a problem with clustering when replacing an instance, when the value is not shared (improvement of clustering, or for other technical reasons) Including) may allow variable. (Kato blog) * -** Equivalence is guaranteed by matching all attributes ** --There is no explicit description in Evans ... -* When a value object is instantiated, it is a design element that only matters what it is, and it does not matter who or what it is. (Evans No.2483-2484) * -* A value object may be a combination of other objects. (Evans No.2490) * --Relationship between window (Entity), window style (ValueObject) and style building material (ValueObject) -** * As the number of attributes increases, the cost will explode when you want to rewrite some attributes. It is said to be a drawback of immutable objects. ** ** --As a workaround, ValueObjectBuilder is implemented. --There is no concept of builder in DDD, but it seems that you can arrange it next to factory in the sense that it hides the concrete generation process.

factory

Factory overview


public class TodoFactory implements EntityFactory<Todo> {

    @Override
    public Todo create() {
        return create("");
    }

    @Override
    public Todo create(final EntityIdentifier<Todo> identifier) {
        Validate.notNull(identifier);

        Title t = new Title("");
        DueDate d = new DueDate();
        TaskList l = new TaskList();
        Memo m = new Memo();
        Favorite f = new Favorite();

        return new Todo(identifier, t, d, l, m, f);
    }

    /**
     * {@link Title}Specify{@link Todo}create
     *
     * @param title
     * @return
     */
    public Todo create(final String title) {
        Validate.notNull(title);

        EntityIdentifier<Todo> identifier = new DefaultEntityIdentifier<>(Todo.class, UUID.randomUUID());
        Title t = new Title(title);
        DueDate d = new DueDate();
        TaskList l = new TaskList();
        Memo m = new Memo();
        Favorite f = new Favorite();

        return new Todo(identifier, t, d, l, m, f);
    }
}

-* Repositories and factories are not domain-derived in their own right (Evans No.3069-3070) * -** Object that generates domain model ** -* Focus on the beginning of the life cycle and use factories to create and reconstruct complex objects and aggregates. (Evans No.3066-3067) * -** If it is easy to generate and there is no variation, you do not have to prepare it. ** ** -** In some cases, it is left to duplicate the domain model ** -* Since the clone method is a shallow copy, you may forget to make a deep copy in consideration of variable objects, or you may miss initialization due to non-final. (Kato blog) * -** It comes out from the story of the defect of the clone method that incorporates the override method ** --Also pointed out that trying to do complex duplication with the clone method of the domain model would violate SRP. -However, if "if it is not the case" and "ValueObjects are all immutable objects", you do not have to think about it. Immutable ValueObjects do not allow elemental changes and do not allow setters. Therefore, since "value change = new object creation", even if clone is a shallow copy, the result after the value change will be the same as that at the time of deep copy. -"If you are creating a variable Value Object" is a point to consider.

repository

-* Repositories and factories are not domain-derived in their own right (Evans No.3069-3070) * -** Object that persists the domain model ** -* The repository encapsulates the huge infrastructure required for access, while providing a means to access persistent objects at the middle and end of the life cycle. (Evans No.3067-3069) * -** Object to restore domain model ** -* How many pages specifically? *

aggregation

-* The scope delimited by the aggregate indicates the extent to which the invariants must be maintained. (Evans No.3073-3074) * -** Object that synthesized domain model ** -* How many pages specifically? * -** Transaction boundaries ** -* How many pages specifically? *

domain service

-* Services are operations that are provided as independent interfaces in the model and do not encapsulate states like entities and value objects. (Evans No.2632-2634) * -* Defined purely in terms of what can be done to the client (Evans No. 2636-2637) * -* Arguments and results should be domain objects (Evans No.2641) * -* Excellent service has three characteristics. (Evans No.2645-2648) *

  1. The operation is related to the concept of a domain, which is not a ** natural part of an entity or value object **.
  2. The interface is defined from the perspective of ** other elements of the domain model **.
  3. There is no ** state in the operation . - Technical services should have no business meaning. (Evans No.2673) * --From the story of modeling the business terms of banks ――In other words, meaningful business behavior should be directed to the domain model. -* When dealing with multiple domain models and realizing business logic ** -* The function to transfer funds from one account to another is a domain service. (Evans No.2680-2681) * -** If it is not suitable for the business logic of the domain model ** -* It becomes difficult to handle if you put the operation of "transfer" in the account object. This operation involves two accounts and some global rules (Evans No.2684-2685) * -** However, do not create domain services as much as possible ** -* How many pages specifically? *

application service

-** When realizing an application use case using a domain model ** -* For example, if a banking application can convert transactions and export them to a spreadsheet file for us to analyze, the export is an application service. In the banking domain, "file format" has no meaning, and business rules are not involved. (Evans No.2677-2680) * -** Very thin ** —— Does that mean you don't traverse layers? Looking at the above description, it's not thin at all, such as generating a spreadsheet. -* How many pages specifically? *

infrastructure service

-* Most of the services discussed in the literature are purely technical and these services belong to the infrastructure layer. (Evans No.2662-2663) * -* If your account balance falls below a certain limit, you may send an email to your customers. This e-mail system is encapsulated by an interface, which may include alternative notification means in some cases. This interface is a service of the infrastructure layer (Evans No.2664-2667) *

interfaces

--GET / POST / PUT / DELETE and other places to receive --Isn't this the socket connection port?

Recommended Posts

DDD personal notes
play framework personal notes
apache POI personal notes crossfish21
First Play Framework personal notes
Sort in List, for personal notes
JUnit 4 notes
java notes
synchronized notes
Rails validation and null: false Personal notes