原则:两件事情
CQS
:命令的定义与实现;查询的定义;(流程编排式用例流程实现)事件
:领域事件的订阅;集成事件的发布与订阅;(发布/订阅式用例流程实现)
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:{所在聚合根名称}:{领域事件名称}[:领域事件说明]
生成【领域事件】代码时,默认会生成【领域事件订阅】代码。所以一般在设计时领域事件订阅代码已经生成过。
待实现...