The most complete DDD practice so far

Author: Zhang Lei (Zhang San) Alibaba Fliggy Technical Team

1. Why do you need DDD

For an architect, how to reduce system complexity in software development is an eternal challenge.

  • Complex system design:  There are many systems, complex business logic, and unclear concepts. Is there any suitable method to help us clarify boundaries, logic, and concepts?
  • Multi-team collaboration:  unclear boundaries, complex system dependencies, and inconsistent language lead to difficulties in communication and understanding. Is there a way to unify business and technical concepts, and everyone communicates in one language. For example: Is the voyage what everyone understands?
  • Design and Implementation Consistency:  PRD, detailed design and code implementation can vary wildly. Is there any way to quickly translate business requirements into design while maintaining consistency between design and code?
  • Unified architecture, reusable assets and scalability:  Currently, it depends on the development of students with good abstraction ability and high programming skills. Is there any good way to guide us in abstraction and implementation.

2. The value of DDD

  • Design method with clear boundaries:  Through domain division, identify which requirements should be in which domains, constantly align the team's cognition of requirements, divide and conquer, and control the scale .
  • Unified language:  The team consciously forms a unified description of things in a bounded context, forming a unified concept (model) .
  • Knowledge precipitation in the business field:  Through repeated demonstration and refinement of the model, the model must be consistent with the real world of the business . Promote knowledge (model) can be well transferred and maintained.
  • Business-oriented modeling:  domain model and data model are separated, and business complexity and technical complexity are separated .

3. DDD architecture

3.1 Layered architecture

  • User interface layer:  call the application layer to complete specific user requests. Contains: controller, remote call service, etc.
  • Application layer App:  As simple as possible, does not contain business rules, but only coordinates tasks and assigns work for the domain objects in the next layer, focusing on the orchestration of the domain layer to complete complex business scenarios. Contains: AppService, message processing, etc.
  • Domain layer Domain:  Responsible for expressing business concepts and business logic, the domain layer is the core of the system. Contains: models, value objects, domain services, events
  • Basic layer:  Provide technical capabilities for all upper layers, including: data operation, sending messages, consuming messages, caching, etc.
  • Call relationship:  user interface layer -> application layer -> domain layer -> base layer
  • Dependency:  user interface layer -> application layer -> domain layer -> base layer

3.2 Hexagonal Architecture

  • Hexagonal Architecture:  The system interacts with the outside world through adapters, encapsulating application services and domain services within the system
  • Layered architecture:  It is still a layered architecture, and its core changes are dependencies.
  • Domain layer dependency inversion:  The domain layer relies on the base layer and inverts the base layer to rely on the domain layer. This simple change makes the domain layer independent of the task layer, and all other layers rely on the domain layer, making the domain layer only express business logic and be stable.

3.3 Call link

Fourth, the basic concept of DDD

4.1 Domain Model

Field (Strategy): Business scope, the scope is the boundary.
Subfields: Fields can be large or small. We disassemble a field to form subfields, and subfields can also be disassembled. When a field is too large, it needs to be refined and dismantled.
Model (tactics): Based on a certain business domain, identify the aggregate, aggregate root, bounded context, entity, and value object of this business domain.

4.1.1 Core domain

The sub-domain that determines the product and the core competitiveness of the company is the core domain, which is the main factor for the success of the business and the core competitiveness of the company. directly add value to the business .

4.1.2 Scope

There is not much personal appeal, and a general function subdomain used by multiple subdomains at the same time is a general domain. For example, permissions, logins, etc. Indirectly generate value for the business .

4.1.3 Support domain

It supports businesses in other fields and has enterprise characteristics, but it is not universal. Indirectly generate value for the business .

4.1.4 Why divide the core domain, general domain and support domain

A business must have its most important part, and decisions can be made based on this division when making daily business judgments and demand priority judgments. For example: A transaction-related requirement and a configuration-related requirement are prioritized. It is obvious that transactions are the core domain and rules are the supporting domain. Similarly, what we think is a supporting domain or a general domain may be a core domain in other companies. For example, authority is a general domain for us, but for companies that specialize in authority systems, this is the core domain.

4.2 Bounded context (strategy)

The division of business boundaries, which can be a domain or a collection of multiple domains. Complex business requires multiple domains to be orchestrated to complete a complex business process. Bounded context can be used as a method of microservice division. Its essence is still high cohesion and low coupling, but the bounded context is only divided at a higher level. How to divide, my method is that a bounded context must support a complete business process, and ensure that the fields involved in this business process are all in a bounded context.

4.3 Entity (ENTITY)

Definition:  An entity has a unique identifier, a life cycle and continuity. For example, for a transaction order, we will give him an order number from the creation of the order and it is unique. This is the unique identifier of the entity . At the same time, the order entity will go from the process of creation, payment, and delivery to the final state. This is the life cycle of the entity . The attribute of the order entity has changed during this process, but the order is still the same order and will not change due to the change of the attribute. This is the continuity of the entity .


The business form of the entity:  the entity can reflect the real form of the business, and the entity is extracted from the use case. Entities in the domain model are carriers of multiple attributes, operations, or behaviors.


Entity code form:  We need to ensure the consistency between entity code form and business form. Then the code of the entity should also have attributes and behaviors, which is what we call the hyperemia model, but in reality we use the anemia model. The disadvantage of the anemia model is that the business logic is scattered, more like a database model. The hyperemia model can reflect the business, but it relies too much on database operations, and in complex scenarios, domain services need to be orchestrated, which will lead to long transactions and affect performance. So we use the congestion model, but the behavior only involves memory operations of business logic.


The operating form of the entity:  the entity has a unique ID. When we modify the attribute of the entity in the process, but the ID will not change, the entity will still be that entity.


The database form of the entity:  when the entity is mapped to the database model, it is generally one-to-one, and there are also one-to-many situations.

4.4 Value Object (VALUEOBJECT)

Definition: An object identified by an object property value that combines multiple related properties into a conceptual whole. Used in DDD to describe specific aspects of the domain, and is an object without an identifier, called a value object. The value object has no unique identifier, no life cycle, and cannot be modified. When the value object changes, it can only be replaced (such as the implementation of String)


The business form of the value object:  the value object is the characteristic that describes the entity. In most cases, an entity has many attributes, which are generally tiled. After the data is classified and aggregated, it can express a business meaning, which is convenient for communication without paying attention to details.


Code form of value object:  the single attribute of an entity is a value object, for example: string, integer, enumeration. A collection of multiple attributes is also a value object. At this time, we design this collection as a CLASS, but without an ID. For example, the flight segment under the commodity entity is a value object. The flight segment is the feature that describes the product. The flight segment does not need an ID and can be directly replaced as a whole. Why is the product an entity instead of describing the characteristics of the order, because we need to express who bought what product, so we need to know which product, so ID is needed to identify uniqueness.


Let's take a look at the following code. The person entity has several value objects of a single attribute, such as Id, name and other attributes; it also contains value objects of multiple attributes, such as the address address.

The operating form of the value object:  after the value object is created, it is not allowed to be modified, and it can only be replaced with another value object as a whole. When we modify the address, just pass in a new address object from the page to replace the address of the calling person object. If we design address as an entity, there must be an ID. Then we need to compare the ID of the address object passed in from the page with the ID of the address object in the person. If they are the same, update them. If they are different, delete the database first and add new data. .


Database form of value objects:  There are two ways to embed and serialize large objects.


Case 1: The person entity object is formed by attribute embedding, and the address value object is directly embedded in the person entity by attribute value.

It is better to use embedded when we only have one address, if multiple addresses must have serialized large objects. At the same time can support search.


Case 2: The person entity object is formed by serializing a large object. After the address value object is serialized into a large object Json string, it is embedded in the person entity.

Supports multiple address storage, does not support search.

Advantages and limitations of Value Objects:


1. Simplify the database design and improve the performance of database operations (multi-table addition and modification, relational table query)
2. Although the database design is simplified, the domain model can still express business
3. The serialization method will make search difficult (through search engines can resolve)

4.5 Aggregates and aggregate roots

The composition of multiple entities and value objects is called aggregation, and the interior of the aggregation must have a certain high cohesion. There must be an entity in this aggregate that is the aggregate root.


The relationship between aggregation and domain: aggregation is also a division of scope, and domain is also a division of scope. Domains and aggregations can be one-to-one or one-to-many relationships


The role of the aggregate root is to ensure the consistency of the internal entities, and only the aggregate root needs to be operated externally.

4.6 Bounded Context, Domain, Aggregation, Entity, Value Object Relationships

A Realm contains a Bounded Context, a Bounded Context contains a Subdomain, a Subdomain contains an Aggregate, and an Aggregate contains Entities and Value Objects

4.7 Event Storm

participant

In addition to domain experts, other participants in event storms can be project team members such as DDD experts, architects, product managers, project managers, developers, and testers

Materials for event storm preparation

A wall and a pen.

Event Storm Concerns

In the process of domain modeling, we need to focus on the language and behavior of this type of business. For example, will certain business actions or behaviors (events) trigger the next business action, and what are the inputs and outputs of this action (event)? Who (entity) issued what action (command) and triggered this action (event)... We can analyze domain objects such as events, commands, and entities in the domain model from these hidden vocabulary.

Entities execute commands to generate events.

Analysis of business scenarios

Identify entities, commands, and events through business scenarios and use cases.

domain modeling

When modeling domains, we will find out the entities that generate commands based on the domain objects generated during the scene analysis process, such as commands, events, etc., and analyze the dependencies between entities to form aggregates, and delineate the bounded context for the aggregates. Establish domain models and dependencies between models. The domain model can guide the design of microservices upwards by using the bounded context, and the design of aggregate roots, entities, and value objects can be guided downwards through aggregation.

5. How to model

  • Sorting out use case scenarios: It is a one-sentence requirement, but we need to gradually obtain clear requirements through dialogue for some vague concepts, and then refine and abstract them.
  • Modeling methodology: lexical analysis (finding nouns and verbs), domain boundaries
  • model validation

5.1 Collaborative order automation case

5.1.1 Domain Modeling

Requirements: We need to automatically assign the system automation failure to manual orders to the second order, avoid manual picking and grabbing orders, and improve the overall performance processing efficiency through automatic assignment.

  • Product A: Read the requirements again.......

  • Developer B: That means assigning the performance order to the second guy right?

  • Product A: No, we also need to automatically divide the order according to a rule, for example, the refunded order is distributed to the refunded second

  • Developer B: Well, then we can do a single-order rule management. For example: Add a rule for ticket refund and order splitting, and add a batch of minor second job numbers in it. Based on its own attributes, the fulfillment order matches the order splitting rules and finds a rule, then selects a secondary job number from the splitting order rules, and writes the secondary job number in the performance slip.

  • Product small A: The order distribution rules also need to have priority. Among them, the small two will be assigned if they are at work, and will not be assigned if they are off work.

  • Developer B: There is no problem with the priority, just sort according to the priority in the method of matching order splitting rules, without affecting the model. And Xiao Er is not simply a job number maintained in the order distribution rules, Xiao Er has a status.

  • Product Small A: It is too cumbersome to add a second person in the order splitting rules. For example, every time a rule is added, you have to pick someone, and people may not remember it. The actual customer service is managed according to the skill group when managing the second person. of.

  • Developer B: Well, I understand, that is to manage Xiao Er by adding a skills group management module. Then configure a skill group through the single rule. Obtaining a small second job number is in the skill group.

  • Developer B: It always feels wrong, because there is a new requirement for automatic order splitting, the performance order depends on the order splitting rules, the performance order should be an independent domain, the order splitting is not the ability to perform the contract, and the performance order actually only needs to know the processor Who it is, he doesn't care much about how it is distributed. The order splitting rule should match a rule based on the properties of the performance order , and then find a second order based on this rule. The performance order is decoupled from the order distribution logic.

  • Product A: Orders should be assigned in turn or those who can do more work will be assigned. Orders and airlines that have been processed by Xiao Er will be given priority.

  • Development Xiao B: The logic of obtaining Xiao Er is becoming more and more complicated. The actual skill group is the core of finding Xiao Er. The core of order distribution rules is to obtain a rule result through the characteristics of the performance order (skill group ID, order distribution strategy, feature rules ). The skill group obtains the secondary job number based on the result of the order splitting rule.

  • Product Small A: Another piece of information is missing, that is, the performance order will be distributed multiple times, and each performance link may be transferred to manual labor. The customer service needs to know that the performance order has been processed multiple times

  • Developer B: That can’t be expressed with the performance order. We need to add a new concept called collaboration order . The coordination order is to coordinate the performance order and promote the progress of the performance order through collaboration.

  • Product Small A: The concept of collaboration order is very good. After Xiao Er gets off work, if the processing is not finished, it can be handed over to others.

  • Developer B: Well, you only need to add behaviors to the collaboration sheet

5.1.2 Domain Division

The communication process is the process of deriving and verifying the model, and finally divides the domain:

5.1.3 Scene sorting

Exhaustively enumerate all scenarios and re-verify whether the model can cover all scenarios.

scene name

Lock

scene action

area

domain service

event

aggregate root

method

Create a collaborative order

none

1. Determine whether the associated business order is illegal

collaborative order

Create a collaborative order

1. Whether the problem classification meets the conditions

(Example: Merchant user initiates self-operated -> Merchant's collaborative order)

2、save

collaborative order

Create a collaborative order

Allocation of collaborative orders

Collaborative single ID

Assign collaboration orders to people.

1. Judging the status of the collaboration order (= pending)

2. Record the operation log

3、save

collaborative order

Allocation of collaborative orders

collaborative order

Allocation of collaborative orders

Acceptance of cooperation form

Collaborative single ID

Dealing with collaboration orders

collaborative order

Acceptance of cooperation form

1. Judging the order status (=pending/failed acceptance)

2. Change order status (pending/failed acceptance -> processing)

3. Record the operation log

4.save

collaborative order

Acceptance of cooperation form

transfer cooperation order

Collaborative single ID

transfer cooperation order

collaborative order

transfer cooperation order

1. Judging the status of the order. (= processing, pending)

  1. Verify that the referral is under the correct organization

2. Change the value object of the coordinator (different people under the same organization, taken from the agent management domain)

3. Record the operation log

4.save

collaborative order

transfer cooperation order

close synergy order

Collaborative single ID

close synergy order

collaborative order

close synergy order

1. Judge order status

(=processing, pending)

2. Change order status

(closure)

3. Record the operation log

4.save

collaborative order

close synergy order

Dealing with collaboration orders

Collaborative single ID

Dealing with collaboration orders

collaborative order

Dealing with collaboration orders

1. Judge order status

(=processing)

2. Change order status (processing -> pending acceptance)

3. Record the operation log

4.save

collaborative order

Dealing with collaboration orders

Rejection of cooperation order

Collaborative single ID

Rejection of cooperation order

collaborative order

Rejection of cooperation order

1. Judge order status

(=to be accepted)

2. Change order status (pending acceptance -> processing)

3. Record the operation log

4.save

collaborative order

Rejection of cooperation order

Complete the cooperation order

Collaborative single ID

Complete the cooperation order

collaborative order

Complete the cooperation order

1. Judge order status

(=to be accepted)

2. Change order status (to be accepted -> completed)

3. Record the operation log

4.save

collaborative order

Complete the cooperation order

Reject Collaboration Order

Collaborative single ID

Reject Collaboration Order

collaborative order

Reject Collaboration Order

1. Judging the order status (= processing, pending)

2. Change order status (rejected)

3. Record the operation log

4.save

collaborative order

Reject Collaboration Order

Reminders

Collaborative single ID

Reminders

collaborative order

Reminders

1. Judging the order status (= processing, pending)

2. Modify reminder value object

3. Record the operation log

4、save

collaborative order

Reminders

6. How to write code

6.1 DDD specification

Each layer defines the corresponding interface. The main purpose is to standardize the code:

  • application:CRQS模式,ApplicationCmdService是command,ApplicationQueryService是query

  • service:是领域服务规范,其中定义了DomainService,应用系统需要继承它。

  • model:是聚合根,实体,值对象的规范。

    • Aggregate和BaseAggregate:聚合根定义
    • Entity和BaseEntity:实体定义
    • Value和BaseValue:值对象定义
    • Param和BaseParam:领域层参数定义,用作域服务,聚合根和实体的方法参数
    • Lazy:描述聚合根属性是延迟加载属性,类似与hibernate。
    • Field:实体属性,用来实现update-tracing

/**
 * 实体属性,update-tracing
 * @param <T>
 */
public final class Field<T> implements Changeable {
    private boolean changed = false;
    private T value;
    private Field(T value){
        this.value = value;
    }
    public void setValue(T value){
        if(!equalsValue(value)){
            this.changed = true;
        }
        this.value = value;
    }
    @Override
    public boolean isChanged() {
        return changed;
    }
    public T getValue() {
        return value;
    }
    public boolean equalsValue(T value){
        if(this.value == null && value == null){
            return true;
        }
        if(this.value == null){
            return false;
        }
        if(value == null){
            return false;
        }
        return this.value.equals(value);
    }
    public static <T> Field<T> build(T value){
        return new Field<T>(value);
    }
}

  • repository

    • Repository:仓库定义
    • AggregateRepository:聚合根仓库,定义聚合根常用的存储和查询方法
  • event:事件处理

  • exception:定义了不同层用的异常

    • AggregateException:聚合根里面抛的异常
    • RepositoryException:基础层抛的异常
    • EventProcessException:事件处理抛的

6.2 工程结构

6.2.1 application模块

  • CRQS模式:commad和query分离。
  • 重点做跨域的编排工作,无业务逻辑

6.2.2 domain模块

域服务,聚合根,值对象,领域参数,仓库定义

6.2.3 infrastructurre模块

所有技术代码在这一层。mybatis,redis,mq,job,opensearch代码都在这里实现,domain通过依赖倒置不依赖这些技术代码和JAR。

6.2.4 client模块

对外提供服务

6.2.5 model模块

内外都要用的共享对象

6.3 代码示例

6.3.1 application示例

public interface CaseAppFacade extends ApplicationCmdService {
    /**
     * 接手协同单
     * @param handleCaseDto
     * @return
     */
    ResultDO<Void> handle(HandleCaseDto handleCaseDto);

}
public class CaseAppImpl implements CaseAppFacade {
    @Resource
    private CaseService caseService;//域服务
    @Resource
    CaseAssembler caseAssembler;//DTO转Param
    @Override
    public ResultDO<Void> handle(HandleCaseDto handleCaseDto) {
        try {
            ResultDO<Void> resultDO = caseService.handle(caseAssembler.from(handleCaseDto));
            if (resultDO.isSuccess()) {
                pushMsg(handleCaseDto.getId());
                return ResultDO.buildSuccessResult(null);
            }
            return ResultDO.buildFailResult(resultDO.getMsg());
        } catch (Exception e) {
            return ResultDO.buildFailResult(e.getMessage());
        }
    }
}

  • mapstruct:VO,DTO,PARAM,DO,PO转换非常方便,代码量大大减少。
  • CaseAppImpl.handle调用域服务caseService.handle

6.3.2 domainService示例

public interface CaseService extends DomainService {
    /**
     * 接手协同单
     *
     * @param handleParam
     * @return
     */
    ResultDO<Void> handle(HandleParam handleParam);
    
}
public class CaseServiceImpl implements CaseService {
    @Resource
	private CoordinationRepository coordinationRepository;

    @Override
    public ResultDO<Void> handle(HandleParam handleParam) {
        SyncLock lock = null;
        try {
            lock = coordinationRepository.syncLock(handleParam.getId().toString());
            if (null == lock) {
                return ResultDO.buildFailResult("协同单handle加锁失败");
            }
            CaseAggregate caseAggregate = coordinationRepository.query(handleParam.getId());
            caseAggregate.handle(handleParam.getFollowerValue());
            coordinationRepository.save(caseAggregate);
            return ResultDO.buildSuccessResult(null);
        } catch (RepositoryException | AggregateException e) {
            String msg = LOG.error4Tracer(OpLogConstant.traceId(handleParam.getId()), e, "协同单handle异常");
            return ResultDO.buildFailResult(msg);
        } finally {
            if (null != lock) {
                coordinationRepository.unlock(lock);
            }
        }
    }
}

  • 领域层不依赖基础层的实现: coordinationRepository只是接口,在领域层定义好,由基础层依赖领域层实现这个接口
  • 业务逻辑和技术解耦: 域服务这层通过调用coordinationRepository和聚合根将业务逻辑和技术解耦。
  • 聚合根的方法无副作用: 聚合根的方法只对聚合根内部实体属性的改变,不做持久化动作,可反复测试。
  • 模型与数据分离:
    • 改变模型:caseAggregate.handle(handleParam.getFollowerValue());
    • 改变数据:coordinationRepository.save(caseAggregate);事务是在save方法上

6.3.3 Aggregate,Entity示例

public class CaseAggregate extends BaseAggregate implements NoticeMsgBuilder {
    private final CaseEntity caseEntity;
    public CaseAggregate(CaseEntity caseEntity) {
        this.caseEntity = caseEntity;
    }
    /**
     * 接手协同单
     * @param followerValue
     * @return
     */
    public void handle(FollowerValue followerValue) throws AggregateException {
        try {
            this.caseEntity.handle(followerValue);
        } catch (Exception e) {
            throw e;
        }
    }
}
public class CaseEntity extends BaseEntity {
    /**
     * 创建时间
     */
    private Field<Date> gmtCreate;
    /**
     * 修改时间
     */
    private Field<Date> gmtModified;
    /**
     * 问题分类
     */
    private Field<Long> caseType;
    /**
     * 是否需要支付
     */
    private Field<Boolean> needPayFlag;
    /**
     * 是否需要自动验收通过协同单
     */
    private Field<Integer> autoAcceptCoordinationFlag;
    /**
     * 发起协同人值对象
     */
    private Field<CreatorValue> creatorValue;
    /**
     * 跟进人
     */
    private Field<FollowerValue> followerValue;
    /**
     * 状态
     */
    private Field<CaseStatusEnum> status;
    /**
     * 关联协同单id
     */
    private Field<String> relatedCaseId;
    /**
     * 关联协同单类型
     * @see 读配置 com.alitrip.agent.business.flight.common.model.dataobject.CoordinationCaseTypeDO
     */
    private Field<String> relatedBizType;

    /**
     * 支付状态
     */
    private Field<PayStatusEnum> payStatus;
    省略....

    public CaseFeatureValue getCaseFeatureValue() {
        return get(caseFeatureValue);
    }
    public Boolean isCaseFeatureValueChanged() {
        return caseFeatureValue.isChanged();
    }

    public void setCaseFeatureValue(CaseFeatureValue caseFeatureValue) {
        this.caseFeatureValue = set(this.caseFeatureValue, caseFeatureValue);
    }

    public Boolean isPayStatusChanged() {
        return payStatus.isChanged();
    }

    public Boolean isGmtCreateChanged() {
        return gmtCreate.isChanged();
    }

    public Boolean isGmtModifiedChanged() {
        return gmtModified.isChanged();
    }

    public Boolean isCaseTypeChanged() {
        return caseType.isChanged();
    }
    省略....

   
    /**
    * 接手
    */
    public void handle(FollowerValue followerValue) throws AggregateException {
        if (isWaitProcess()||isAppointProcess()) {
            this.setFollowerValue(followerValue);
            this.setStatus(CaseStatusEnum.PROCESSING);
            this.setGmtModified(new Date());
            initCaseRecordValue(CaseActionNameEnum.HANDLE, null, followerValue);
        } else {
            throwStatusAggregateException();
        }
    }
    省略....
}

  • 充血模型VS贫血模型:
    • 充血模型:表达能力强,代码高内聚,领域内封闭,聚合根内部结构对外不可见,通过聚合根的方法访问,适合复杂企业业务逻辑。
    • 贫血模型:业务复杂之后,逻辑散落到大量方法中。
  • 规范大于技巧:DDD架构可以避免引入一些其他概念,系统只有域,域服务,聚合根,实体,值对象,事件来构建系统。

聚合根的reconProcess的方法的业务逻辑被reconHandler和reconRiskHandler处理,必然这些handler要访问聚合根里面的实体的属性,那么逻辑就会散落。修改后:

没有引入其他概念,都是在聚合根里面组织实体完成具体业务逻辑,去掉了handler这种技术语言。

  • 聚合根和实体定义的方法是具备单一原则,复用性原则与使用场景无关,例如:不能定义手工创建协调单和系统自动创建协同单,应该定义创建协同单。
  • Update-tracing: handle方法修改属性后,然后调用 coordinationRepository.save(caseAggregate),我们只能全量属性更新。Update-tracing是监控实体的变更。 Entiy定义属性通过Field进行包装实现属性的变更状态记录,结合mapstruct转换PO实现Update-tracing。

修改了mapstruct生成转换代码的源码,修改后生成的代码:

当属性被改变后就转换到po中,这样就可以实现修改后的字段更新。修改后的mapstruct代码地址:[email protected]:flight-agent/mapstruct.git

  • idea的get和set方法自动生成: 由于使用field包装,需要自定义get和set生成代码

6.3.4 Repository示例

public interface CoordinationRepository extends Repository {  
	/**
     * 保存/更新
     * @param aggregate
     * @throws RepositoryException
  	 */
 	void save(CaseAggregate aggregate) throws RepositoryException;
}
@Repository
public class CoordinationRepositoryImpl implements CoordinationRepository {
	@Override
    public void save(CaseAggregate aggregate) throws RepositoryException {
        try {
            
            //聚合根转PO,update-tracing技术
            CasePO casePO = caseConverter.toCasePO(aggregate.getCase());
            CasePO oldCasePO = null;
            if (aggregate.getCase().isAppended()) {
                casePOMapper.insert(casePO);
                aggregate.getCase().setId(casePO.getId());
            } else {
                oldCasePO = casePOMapper.selectByPrimaryKey(casePO.getId());
                casePOMapper.updateByPrimaryKeySelective(casePO);
            }
            // 发送协同单状态改变消息
            if (CaseStatusEnum.FINISH.getCode().equals(casePO.getStatus())
                || CaseStatusEnum.WAIT_DISTRIBUTION.getCode().equals(casePO.getStatus())
                || CaseStatusEnum.PROCESSING.getCode().equals(casePO.getStatus())
                || CaseStatusEnum.APPOINT_PROCESS.getCode().equals(casePO.getStatus())
                || CaseStatusEnum.WAIT_PROCESS.getCode().equals(casePO.getStatus())
                || CaseStatusEnum.CLOSE.getCode().equals(casePO.getStatus())
                || CaseStatusEnum.REJECT.getCode().equals(casePO.getStatus())
                || CaseStatusEnum.PENDING_ACCEPTANCE.getCode().equals(casePO.getStatus())) {

                FollowerDto followerDto = new FollowerDto();
                followerDto.setCurrentFollowerId(aggregate.getCase().getFollowerValue().getCurrentFollowerId());
                followerDto.setCurrentFollowerGroupId(aggregate.getCase().getFollowerValue().getCurrentFollowerGroupId());
                followerDto.setCurrentFollowerType(aggregate.getCase().getFollowerValue().getCurrentFollowerType());
                followerDto.setCurrentFollowerName(aggregate.getCase().getFollowerValue().getCurrentFollowerName());
                //拒绝和关闭都使用CLOSE
                String tag = CaseStatusEnum.codeOf(casePO.getStatus()).name();
                if(CaseStatusEnum.REJECT.name().equals(tag)){
                    tag = CaseStatusEnum.CLOSE.name();
                }
                statusChangeProducer.send(CaseStatusChangeEvent.build()
                    .setId(casePO.getId())
                    .setFollowerDto(followerDto)
                    .setStatus(aggregate.getCase().getStatus().getCode())
                    .setCaseType(aggregate.getCase().getCaseType())
                    .setOldStatus(null != oldCasePO ? oldCasePO.getStatus() : null)
                    .setAppointTime(aggregate.getCase().getAppointTime()), (tag));
            }

            // 操作日志
            if (CollectionUtils.isNotEmpty(aggregate.getCase().getCaseRecordValue())) {
                CaseRecordValue caseRecordValue = Lists.newArrayList(aggregate.getCase().getCaseRecordValue()).get(0);
                caseRecordValue.setCaseId(casePO.getId());
                recordPOMapper.insert(caseConverter.from(caseRecordValue));
            }

        } catch (Exception e) {
            throw new RepositoryException("", e.getMessage(), e);
        }
    }
}

  • CoordinationRepository接口定义在领域层
  • CoordinationRepositoryImpl实现在基础层:数据库操作都是基于聚合根操作,保证聚合根里面的实体强一致性。

七、最后结束语

  • 好的模型,可以沉淀组织资产,不好的模型,逐渐成为负债
  • 功能才是表象,模型才是内在
  • 建模过程是不断猜想与反驳的过程
  • 演化观点是建模过程的基本心智模式

Guess you like

Origin blog.csdn.net/AlibabaTech1024/article/details/125674376