结合了整洁架构风格 、CQRS风格以及分层架构风格,并采用依赖原则分4层,如下:
- interface-adapter 接口适配层(适配dubbo、rest接口等协议)
- application 应用层(实现用例的地方,eg:电商场景里的用户下单是个用例)
- domain 领域层(写领域逻辑的地方,eg:电商场景里的用户下单包含订单逻辑、商品逻辑、以及优惠逻辑等)
- infrastructure 基础层(放Cache、MQ框架、数据库持久实现等的地方)
模块依赖关系以及与整洁架构风格模块对应关系:
- 执行ddd-module-demo/ddd-bootstrap下的App类的main方法
- 创建一个商品
curl -H "Content-Type:application/json" -X POST http://localhost:8080/product/create --data '{"name":"test","description":"test description"}'
代码路径:ProductController.createProduct
- 获取商品信息(点击)
http://localhost:8080/product/get?productId=1
代码路径:ProductController.getProduct
- 获取商品信息(命令行)
curl -H "Content-Type:application/json" -X GET 'http://localhost:8080/product/get?productId=1'
代码路径:ProductController.getProduct
- 分页查询商品信息(点击)
代码路径:ProductController.queryProducts
- 修改商品
curl -H "Content-Type:application/json" -X POST http://localhost:8080/product/update --data '{"productId":"1","name":"test","description":"test description"}'
代码路径:ProductController.updateProduct
- 禁用商品
curl -H "Content-Type:application/json" -X POST 'http://localhost:8080/product/deactivate?productId=1'
代码路径:ProductController.deactivateProduct
- 启用商品
curl -H "Content-Type:application/json" -X POST 'http://localhost:8080/product/activate?productId=1'
代码路径:ProductController.activateProduct
- 删除商品
curl -H "Content-Type:application/json" -X POST 'http://localhost:8080/product/delete?productId=1'
代码路径:ProductController.deleteProduct
注意:需要提前将jar mvn clean install -Dmaven.test.skip=true
进maven本地库或私库里
<dependency>
<groupId>com.runssnail.ddd</groupId>
<artifactId>ddd-spring</artifactId>
<version>${ddd.version}</version>
</dependency>
增加spring配置,加入配置后,默认自动从spring上下文收集CommandHandler和EventHandler(需要在类上增加spring注解@Service、@Component等)
<bean id="commandBus" class="com.runssnail.ddd.spring.CommandBusFactoryBean"/>
<bean id="eventBus" class="com.runssnail.ddd.spring.EventBusFactoryBean"/>
<dependency>
<groupId>com.runssnail.ddd</groupId>
<artifactId>ddd-spring-boot-starter</artifactId>
<version>${ddd.version}</version>
</dependency>
一个Command对象对应一个用例的请求数据
@Data
public class CreateProductCommand extends AbstractCommand<Result> {
private String name;
private String description;
@Override
public Class<Result> resultType() {
return Result.class;
}
}
表示一个领域事件,用例完成后,发布一个领域事件
@Getter
public class ProductCreatedEvent extends AbstractEvent {
private String productId;
public ProductCreatedEvent(String productId) {
this.productId = productId;
}
}
分发Command到对应的CommandHandler里去处理业务
@Component
public class ProductApplicationService {
@Autowired
private CommandBus commandBus;
/**
* 创建商品
*
* @param command
* @return
*/
@Transactional
public Result<Product> createProduct(CreateProductCommand command) {
return commandBus.dispatch(command);
}
}
@Component
public class CreateProductCommandHandler extends BaseCommandHandler<CreateProductCommand, Result> {
// 这里省略...
@Autowired
private EventBus eventBus;
@Override
public Class<CreateProductCommand> supportCommand() {
return CreateProductCommand.class;
}
@Override
public Result<String> doHandle(CreateProductCommand command) {
// 转换领域对象
Product product = this.productDomainService.createProduct(command);
// 保存数据
productRepository.save(product);
// 发布领域事件
eventBus.publish(new ProductCreatedEvent(product.getProductId()));
return Result.success(product.getProductId());
}
}
用来实现用例,一个Command对应一个CommandHandler
@Component
public class CreateProductCommandHandler extends BaseCommandHandler<CreateProductCommand, Result> {
// 这里省略...
@Override
public Class<CreateProductCommand> supportCommand() {
return CreateProductCommand.class;
}
@Override
public Result<String> doHandle(CreateProductCommand command) {
// do bussniess
return Result.success(product.getProductId());
}
}
用来拦截Command,支持多个CommandInterceptor处理同一个Command
@Component
@Order(1)
public class CreateProductInterceptor implements CommandInterceptor<CreateProductCommand, Result> {
private static final Logger log = LoggerFactory.getLogger(CreateProductInterceptor.class);
@Override
public Class<CreateProductCommand> supportCommandType() {
return CreateProductCommand.class;
}
@Override
public void beforeHandle(CreateProductCommand command) {
log.info("CreateProductInterceptor.preHandle");
}
@Override
public void afterHandle(CreateProductCommand command, Result result) {
log.info("CreateProductInterceptor.postHandle");
}
}
用来验证Command参数完整性或者业务前置校验,默认支持Hibernate Validator
@Component
public class CreateProductCommandValidator implements CommandValidator<CreateProductCommand> {
@Override
public Class<CreateProductCommand> supportType() {
return CreateProductCommand.class;
}
@Override
public void validate(CreateProductCommand createProductCommand) throws IllegalArgumentException, BizException {
Validate.notNull(createProductCommand);
Validate.notNull(createProductCommand.getName());
Validate.isTrue(createProductCommand.getName().length() <= 10);
}
}
用来组装查询请求数据,将领域实体对象转换成DTO后返回给外部使用,将领域对象封装在内部
@Component
public class CreateProductAssembler implements Assembler<Product, ProductDTO> {
@Override
public ProductDTO assemble(Product product) {
ProductDTO target = new ProductDTO();
BeanUtils.copyProperties(product, target);
return target;
}
}
实现领域实体对象和数据对象之间的转换
@Component
public class ProductConverter implements Converter<Product, ProductDO> {
@Override
public ProductDO serialize(Product product) {
// todo 领域对象转换成数据对象
ProductDO productDO = new ProductDO();
productDO.setId(product.getProductId());
return productDO;
}
@Override
public Product deserialize(ProductDO productDO) {
// todo 数据对象转换成领域对象
return new Product(productDO.getId());
}
}
用来判断修改数据时,是否产生了并发问题。
@Override
public void remove(Product product) throws ConcurrencyConflictException {
int count = this.productDOMapper.deleteById(product.getId(), product.getOperator(), product.getConcurrencyVersion());
ConcurrencyConflicts.check(count, "remove Product, id={}, concurrencyVersion={}", product.getId(), product.getConcurrencyVersion());
}