Skip to content

Latest commit

 

History

History
465 lines (392 loc) · 12.8 KB

02_应用层编码指南.md

File metadata and controls

465 lines (392 loc) · 12.8 KB

应用层编码指南

原则:两件事情

  1. CQS:命令的定义与实现;查询的定义;(流程编排式用例流程实现)
  2. 事件:领域事件的订阅;集成事件的发布与订阅;(发布/订阅式用例流程实现)

辅助代码生成

cap4j插件cap4j-ddd-codegen提供快速创建模型设计元素框架代码。设计元素支持如下:

应用层:查询 应用层:命令 应用层:防腐端 应用层:集成事件 应用层:集成事件订阅 领域层:领域服务(警惕滥用导致领域模型贫血化) 领域层:聚合工厂 领域层:领域事件&订阅 领域层:实体规格约束

设计语法详见帮助:mvn cap4j-ddd-codegen:help。后续根据需要,cap4j也会采纳并支持更多好的设计元素辅助代码生成。

mvn cap4j-ddd-codegen:gen-design -Ddesign=literalDesign1;[literalDesign2;][...][literalDesignN;]
mvn cap4j-ddd-codegen:gen-design -DdesignFile=/path/to/designFile

依赖cap4j-ddd-codegen:gen-design,我们可以将更多的精力放到模型的面向对象设计上。

设计示例

待补充

中介者

cap4j提供中介者模式来方便调用各个模型设计元素。

聚合工厂中介者: Mediator.factories().create(entityPayload); // 创建聚合根实例

仓储中介者: Mediator.repositories().find*(); // 查询检索聚合根

领域服务中介者: Mediator.service().getService(domainServiceClass); // 查询检索聚合根

工作单元实例: Mediator.uow().persist(entity)、remove(entity)、 save(); // 聚合根实例字段变更同步持久化

集成事件中介者: Mediator.events().attach(eventPayload); // 发布集成事件

命令中介者: Mediator.commands().send(commandRequest); // 发出命令请求

查询中介者: Mediator.queries().send(queryRequest); // 发出命令请求

请求中介者: Mediator.requests().send(request); // 发出请求

命令

代码位置规范${basePackage}.application.commands

重点关注使用聚合工厂中介者仓储中介者工作单元实例

在某些情形下会使用到请求中介者执行外部服务请求,以及集成事件中介者对外发出集成事件。

特别需慎用领域服务中介者,警惕领域模型贫血化问题。

辅助代码生成

mvn cap4j-ddd-codegen:gen-design -Ddesign=cmd:{命令名称}[:{命令说明}]

代码示例

mvn cap4j-ddd-codegen:gen-design -Ddesign=cmd:order.PlaceOrder

命令参数类

package org.netcorepal.cap4j.ddd.example.application.commands.order;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.netcorepal.cap4j.ddd.application.RequestParam;

/**
 * PlaceOrderCmd命令请求参数
 * todo: 命令描述
 *
 * @author cap4j-ddd-codegen
 * @date 2024/09/14
 */
@Data
@Builder
public class PlaceOrderCmdRequest implements RequestParam<PlaceOrderCmdResponse> {

}

命令响应类

package org.netcorepal.cap4j.ddd.example.application.commands.order;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * PlaceOrderCmd命令响应
 * todo: 命令描述
 *
 * @author cap4j-ddd-codegen
 * @date 2024/09/14
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PlaceOrderCmdResponse {
    boolean success;
}

命令处理类

package org.netcorepal.cap4j.ddd.example.application.commands.order;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.netcorepal.cap4j.ddd.Mediator;
import org.netcorepal.cap4j.ddd.application.command.Command;
import org.springframework.stereotype.Service;

/**
 * PlaceOrderCmd命令请求实现
 * todo: 命令描述
 *
 * @author cap4j-ddd-codegen
 * @date 2024/09/14
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class PlaceOrderCmdHandler implements Command<PlaceOrderCmdRequest, PlaceOrderCmdResponse> {

    @Override
    public PlaceOrderCmdResponse exec(PlaceOrderCmdRequest cmd) {
        Mediator.uow().save();
        
        return null;
    }
}

查询

基于CQS模式,查询可独立实现。不必依赖领域模型(领域层)实现,可直接依据DI原则,由适配层实现即可。

应用层仅需提供查询(Query)出入参(Request\Response)声明定义。

辅助代码生成

gen-design命令

mvn cap4j-ddd-codegen:gen-design -Ddesign=qry:{查询名称}[:{查询说明}]

代码示例

mvn cap4j-ddd-codegen:gen-design -Ddesign=qry:order.GetOrder

查询参数类

package org.netcorepal.cap4j.ddd.example.application.queries.order;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.netcorepal.cap4j.ddd.application.RequestParam;


/**
 * todo: 查询请求参数描述
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GetOrderQryRequest implements RequestParam<GetOrderQryResponse> {
    Long id;
}

查询响应类

package org.netcorepal.cap4j.ddd.example.application.queries.order;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * todo: 查询响应描述
 *
 * @author cap4j-ddd-codegen
 * @date 2024/09/09
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GetOrderQryResponse {
    Long id;
}

防腐端

代码位置规范${basePackage}.application.distributed.clients

所有外部依赖服务的防腐端构成防腐层ACL。

辅助代码生成

mvn cap4j-ddd-codegen:gen-design -Ddesign=cli:{防腐端名称}[:{防腐端说明}]

代码示例

mvn cap4j-ddd-codegen:gen-design -Ddesign=cli:User

请求类

package org.netcorepal.cap4j.ddd.example.application.distributed.clients;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.netcorepal.cap4j.ddd.application.RequestParam;

/**
 * todo: 防腐端请求参数描述
 *
 * @author cap4j-ddd-codegen
 * @date 2024/9/9
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserCliRequest implements RequestParam<UserCliResponse> {
    Long id;
}

响应类

package org.netcorepal.cap4j.ddd.example.application.distributed.clients;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * todo: 防腐端响应描述
 *
 * @author cap4j-ddd-codegen
 * @date 2024/9/9
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserCliResponse {
    Long id;
}

请求处理类

package org.netcorepal.cap4j.ddd.example.adapter.application.distributed.clients;

import org.netcorepal.cap4j.ddd.example.application.distributed.clients.UserCliRequest;
import org.netcorepal.cap4j.ddd.example.application.distributed.clients.UserCliResponse;
import org.netcorepal.cap4j.ddd.application.RequestHandler;
import org.springframework.stereotype.Service;

/**
 * todo: 防腐端描述
 *
 * @author cap4j-ddd-codegen
 * @date 2024/9/9
 */
@Service
public class UserCliHandler implements RequestHandler<UserCliRequest, UserCliResponse> {
    @Override
    public UserCliResponse exec(UserCliRequest userCliRequest) {
        return null;
    }
}

集成事件声明

代码位置规范${basePackage}.application.distributed.events

仅需标记@IntegrationEvent 注解,即可定义集成事件。

辅助代码生成

mvn cap4j-ddd-codegen:gen-design -Ddesign=ie:{集成事件名称}[:mq-topic[:mq-consumer[:集成事件说明]]]

代码示例

mvn cap4j-ddd-codegen:gen-design -Ddesign=ie:OrderPlaced:cap4j-example-order-placed
package org.netcorepal.cap4j.ddd.example.application.distributed.events;

import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRelease;
import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent;
import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.events.OrderClosedDomainEvent;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * todo: 集成事件描述
 *
 * @author cap4j-ddd-codegen
 * @date 2024/09/09
 */
@IntegrationEvent(value = "cap4j-example-order-placed", subscriber = "${spring.application.name}")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class OrderPlacedIntegrationEvent {
    private Long id;
}

集成事件发布

显式发布集成事件

代码位置规范${basePackage}.application.subscribers.domain包中的领域事件订阅实现逻辑中;

代码位置规范${basePackage}.application.commands包中的命令实现逻辑中。

代码示例

package org.netcorepal.cap4j.ddd.example.application.subscribers.domain;

import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.events.OrderPlacedDomainEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

/**
 * todo: 领域事件描述
 */
@Service
@RequiredArgsConstructor
public class OrderPlacedDomainEventSubscriber {

    @EventListener(OrderPlacedDomainEvent.class)
    public void on(OrderPlacedDomainEvent event) {
        Mediator.events().attach(OrderPlacedIntegrationEvent.builder()
                .id(event.getId())
                //...
                .build());
    }

}

Mediator.events() 返回IntegrationEventSupervisor接口。

声明式发布集成事件

集成事件声明上标记@AutoRelease注解。cap4j将会利用反射技术关联到对应领域事件的发布并自动触发集成事件的发布。

代码示例

package org.netcorepal.cap4j.ddd.example.application.distributed.events;

import org.netcorepal.cap4j.ddd.application.event.annotation.AutoRelease;
import org.netcorepal.cap4j.ddd.application.event.annotation.IntegrationEvent;
import org.netcorepal.cap4j.ddd.example.domain.aggregates.order.events.OrderClosedDomainEvent;
import org.springframework.core.convert.converter.Converter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * todo: 集成事件描述
 *
 * @author cap4j-ddd-codegen
 * @date 2024/09/09
 */
@IntegrationEvent(value = "cap4j-example-order-placed", subscriber = "${spring.application.name}")
@AutoRelease(sourceDomainEventClass = OrderPlacedDomainEvent.class)
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class OrderPlacedIntegrationEvent implements Converter<OrderPlacedDomainEvent, OrderPlacedIntegrationEvent> {
    private Long id;

    @Override
    public OrderPlacedIntegrationEvent convert(OrderPlacedDomainEvent source) {
        return OrderPlacedIntegrationEvent.builder()
                .id(source.getId())
                //...
                .build();
    }
}

集成事件订阅

代码位置规范${basePackage}.application.subscribers.integration

实现对应事件的EventSubscriber 服务并标记@Service注解注册到Spring IoC容器中,即可实现对应事件发生时订阅逻辑的自动触发调用。

辅助代码生成

gen-design命令

mvn cap4j-ddd-codegen:gen-design -Ddesign=ies:{集成事件名称}[:mq-topic[:mq-consumer[:集成事件说明]]]

代码示例

mvn cap4j-ddd-codegen:gen-design -Ddesign=ies:OrderPlaced
package org.netcorepal.cap4j.ddd.example.application.subscribers.integration;

import org.netcorepal.cap4j.ddd.example.application.distributed.events.OrderPlacedIntegrationEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

/**
 * todo: 集成事件描述
 */
@Service
@RequiredArgsConstructor
public class OrderPlacedIntegrationEventSubscriber {

    @EventListener(OrderPlacedIntegrationEvent.class)
    public void on(OrderPlacedIntegrationEvent event) {
        
    }

}

领域事件订阅,

代码位置规范${basePackage}.application.subscribers.domain 领域事件与集成事件的订阅在业务上的职责与概念并无差别,接口也并无差别。

直接参考集成事件订阅说明。

辅助代码生成

gen-design命令

mvn cap4j-ddd-codegen:gen-design -Ddesign=des:{所在聚合根名称}:{领域事件名称}[:领域事件说明]

生成【领域事件】代码时,默认会生成【领域事件订阅】代码。所以一般在设计时领域事件订阅代码已经生成过。

SAGA

待实现...