1 introduction

Entities are domain objects in the domain model.

MVC developers tend to focus on the data, not the domain. Because in software development, DB dominates. It is the attributes of the data (that is, the columns of the database) and associations (foreign key associations) that are considered first, not the notion of a rich domain. As a result, the data model is reflected directly in the object model, and entities that represent the domain model contain a large number of getters/setters. Adding getters/setters to your entity model is not a big mistake, but it is not DDD practice.

Team members initially overemphasized the role of entities and ignored value objects. Entities were being abused by the team due to DB and persistence frameworks, so they started discussing how to avoid using entities on a large scale…

2 Why use entities

When we need to consider the personality characteristics of an object, or need to distinguish between different objects, we introduce the domain concept of entity.

An entity is a unique thing that can change over a period of time. These objects are not important for their attributes, but for their continuity and identity, spanning and even beyond the software lifecycle.

It is also the unique identity and mutability characteristics that distinguish entity objects from value objects.

Entity modeling is not always a perfect solution. Many times, a domain concept should be modeled as a value object rather than an entity object. This means that DDD does not always meet business requirements and may be more applicable when developing CRUD software systems. If CRUD is applied to the wrong system – the more complex system that requires DDD – we may regret it. CRUD systems cannot create good business models because they only start with data.

When DDD is available, we turn the data model into a solid model.

Distinguish objects by identifiers, not attributes: Identifiers should be the primary model definition at this point. While maintaining simple class definitions, focus on continuity and unique identification of objects throughout their life cycle. Entity objects should not be distinguished by their state and history… The model should define what is the same thing.

So how do you use and design entities correctly?

3 Unique Identifier

In the early stage of entity design, we pay attention to the main attributes and behaviors that can reflect the uniqueness of entity identity and how to query the entity, and ignore the secondary attributes and behaviors.

When designing an entity, consider the essential characteristics of the entity first, especially its unique identification and search for the entity, rather than focusing on the attributes and behavior of the entity from the beginning. Attributes and behaviors are added only if they are useful to the essential characteristics of the entity [Evans, p.93].

Find a variety of ways to achieve unique identification, while considering how to maintain uniqueness over the life of the entity. The unique identity of an entity does not necessarily help in finding and matching the entity. The use of a unique identifier for entity matching usually depends on the readability of the identifier. Such as:

  • If the system provides look-up by name, the unique identification of a Person entity may not be a Person’s name because there are many duplicate names
  • If a system provides a search function based on the Company tax number, the tax number can be used as the unique identification of the Company entity

A value object can be used to hold a unique identifier for an entity. Value objects are immutable, which ensures the stability of entity identity and allows for centralized processing of identity-related behaviors. This avoids leaking identity identity-related behavior to other parts of the model or to the client.

3.1 Creating an Entity Identity Policy

In general, every technical solution has side effects. When relational DB is used for object persistence, for example, this side effect spills over into the domain model. The time of identity generation, the reference identity of relational data, the role of ORM in the identity creation process, and how to ensure the stability of unique identity need to be considered before creation.

  • For details, see

DDD domain-driven design practice – a common strategy for creating an entity identity

3.2 Identity Stability

The vast majority of scenarios should not modify the unique identity of the entity, which can be maintained throughout the life cycle of the entity.

There are a few simple steps you can take to ensure that the entity identity is not modified. Identified setter methods can be hidden from the user. You can also add logic to setter methods to ensure that the identity will not be updated if it already exists. For example, you can use assertions:

  • usernameAttributes areUserThe domain identity of the entity, which can be changed only once and only within the User object. A setter methodsetUsernameIt is self-encapsulated and invisible to the client. The setter method checks when the entity’s public method self-delegates to itusernameProperty to see if it has been assigned. If yes, the domain identifier for the User object already exists, and the program throws an exception.

This setter method does not prevent Hibernate from rebuilding the object, because when the object is created, its properties are created with default values and a no-argument constructor, sousernameThe initial value of the property is NULL. Hibernate will then call the setter method, and since the USERNAME property is null at this point, the setter method will execute correctly and the username property will be given the correct identifier value.

4 Entities in various states

The entity form of DDD varies with the design process.

4.1 Service Form

In strategic design, entities are an important object of the domain model. Entities in the domain model are carriers of multiple properties, operations, or behaviors.

In event storms, you can find out the business entity objects that generate these behaviors based on commands, operations, or events, and then cluster multiple entity objects and value objects that are highly dependent and closely associated with services based on service rules to form aggregation.

Entity and value objects are the basic units that make up the domain model.

4.2 Code Form

The entity class contains the attributes and methods of the entity through which the entity’s own business logic is implemented. Congestion model is usually used:

  • All business logic associated with the entity is implemented in the methods of the entity class
  • Domain logic that spans multiple entities is implemented in domain services

4.3 Operating Mode

Entities exist in the form of DO (domain objects), and each entity object has a unique ID. Entities can be modified multiple times, so an entity object can be significantly different from its previous state. But they have the same identity, so they are always the same entity.

For example, the commodity is an entity of the commodity context, identified by a unique commodity ID. No matter how the data of the commodity (such as price) changes, the commodity ID will not change, and it is always the same commodity.

4.4 Database Configuration

DDD builds the domain model, builds entity objects and behaviors for business scenarios, and maps entity objects to data persistence objects.

When the domain model maps to the data model, an entity may correspond to 0, 1, or multiple database persistent objects. Most of the time entities and persistent objects are one-to-one. In some scenarios, some entities are just a running entity in transient static memory and do not need persistence. For example, a discount entity is generated after calculation based on multiple price configuration data.

In some complex scenarios, entities and persistent objects may be one-to-many or many-to-one:

  • More than a pair of

User user and role Two persistent objects can generate permission entities. One entity corresponds to two persistent objects

  • For one more

Sometimes, to avoid database query, the customer information and account information are saved in the same database table. The customer and account entities can be generated from a persistent object as required

Explore the nature of the entity

Start by modeling a large number of entity-relationships in Your Java code. Too much focus is placed on database, table, column, and object mappings. The resulting model is really just an anemic domain model with lots of getters/setters. They should think more about DDD. At the time they separated the security handling mechanism from the core domain, they learned how to use a common language to better aid modeling. But it would be a mistake to think of an object as nothing more than a set of named classes and operations defined on them. There are many other things that can be included in the domain model. Team discussions and specification documents can help us create a more meaningful common language. At the end of the day, the team can talk directly to the common language, and the model reflects the common language very accurately. If there are specific domain scenarios that will continue to be used in the future, you can document them in a lightweight document. A common language in a simple form can be a set of terms and some simple use case scenarios. However, we would be wrong to assume that the common language consists only of terms and use case scenarios. In the end, the common language should be reflected directly in the code, and it is difficult, if not impossible, to keep design documents up to date.

5 Creating Entities

When creating a new entity, we always expect to initialize enough entity state through the constructor, because this helps identify the entity and makes it easier for clients to find it.

When using a strategy that generates unique identifiers early, the constructor accepts at least one unique identifier parameter. If it is possible to find the entity in other ways, such as by name or description, these parameters should be passed to the constructor as well.

Sometimes an entity maintains one or more invariants (a state in which a transaction must be consistent throughout the entity’s life).

Immutable conditions are mainly of concern to aggregation, but since aggregation roots are usually also entities, we will mention them here.

If the entity’s immutable conditions require that none of the objects contained in the entity be null or computed by other states, those states need to be passed to the constructor as arguments.

public class User extends Entity {...// Every User object must contain tenantld, username, password, and Person attributes.
	// That is, after the User object is properly instantiated, these attributes cannot be null
	This is guaranteed by the constructor of the User object and the setter method corresponding to the instance variable
	protected User (Tenantld aTenantld, String aUsername, String aPassword, Person aPerson) (
		this(a);
		this.setPassword(aPassword);
		this.setPerson(aPerson);
		this.setTenantld(aTenantld);
		this.setUsername(aUsername);
		this.initialize(); }...protected void setPassword(String aPassword) { 
		if (aPassword == null) {
			throw new 11legalArgumentException(
				"The password may not be set to null.");this.password = aPassword;
	)
	
	protected void setPerson(Person aPerson) (
		if (aPerson == null) ( 
			throw new IllegalArgumentException(
			"The person may not be set to null.");
		}
		this.person = aPerson;
	}
	
	protected void setTenantld(Tenantld aTenantld) ( 
		if (aTenantld == null)(
			throw new IllegalArgumentException(
				"The tenantld may not be set to null."); }
		this.tenantld = aTenantld;
	}
	protected void setUsername(String aUsername) (
		if (this.username ! =null) (
			throw new IIlegalStateException(
				"The username may not be changed.n); } if (aUsername == null) ( throw new IllegalArgumentException( "The username may not be set to null."); } this.username = aUsername; }Copy the code

The User object demonstrates self-encapsulation. When the constructor assigns a value to an instance variable, it delegates the operation to the setter method corresponding to the instance variable, thus ensuring that the instance variable is self-encapsulated. Self-encapsulation of instance variables uses setter methods to determine when to assign values to instance variables. Each setter method does a non-NULL check on passed parameters “on behalf of the entity,” an assertion called Guard. The self-encapsulation technique of setter methods can become quite complex. For very complex entity creation cases, we can use factories. In the example above, did you notice that the constructor of the User object is declared protected? The Tenant entity is the factory of the User entity and the only class in the same module that has access to the User constructor. In this way, only Tenant can create a User instance.

public class Tenant extends Entity {
	// This factory simplifies the creation of users while ensuring the correctness of Tenantld in User and Person objects
	// This factory can reflect the common language.
	public User registerUser( String aUsername, String aPassword, Person aPerson) {
		aPerson.setTenantld(this.tenantld());
		
		User user = new User(this.tenantld(), aUsername, aPassword, aPerson);
		return user;
}
Copy the code

reference

  • Tech.meituan.com/2017/12/22/…
  • Implementing Domain-Driven Design
  • Entities and value objects: Look at system design from the base unit of the domain model
  • Blog.csdn.net/Taobaojishu…