COLA开发流程总结
COLA发起者的博客
COLA是一套用于帮助你实践DDD落地的项目架构,在开发COLA之前,我们先再熟悉一下基于COLA创建的项目的结构:
demo-client:
- api:存放的是对外暴露的接口
- dto.domainmodel:用来做数据传输的轻量级领域对象。
- to.domainevent: 用来做数据传输的领域事件。
demo-app:
- service:接口实现的facade,没有业务逻辑,可以包含对不同终端的adapter。
- eventhandler:处理领域事件,包括本域的和外域的。
- executor:用来处理命令(Command)和查询(Query),对复杂业务,可以包含Phase和Step。
- interceptor: COLA提供的对所有请求的AOP处理机制。
- validator:用来对传入的命令进行参数校验。
demo-domain:
- domain:领域实体。
- domainservice: 领域服务,用来提供更粗粒度的领域能力。
- gateway:对外依赖的网关接口,包括存储、RPC、Search等,可以认为是对infrastructure的依赖反转。
demo-infrastructure:
- config:配置信息相关
- message:消息处理相关。
- repository:存储相关,是gateway的特化,主要用来做本域的数据CRUD操作。
- gateway:对外依赖的网关接口(demo-domain里的gateway)的实现。
除此之外,我们还要熟悉一下COLA中关于命名的规范:
在COLA架构中,我们也能找到以前熟悉的VO,DTO等对象,只不过在COLA中他们的后缀名变了:
- VO->CO,COLA中CO对象用于展示给前端。
- DTO->Cmd,COLA中Cmd对象用于前端和后端的数据交互。
- Entiry->DO,数据持久化对象。
此外,对于领域对象domain,比如UserDomain,我们在COLA中称之为UserE。
如何基于COLA架构实现一个CRUD
在COLA中,比如我们想创建一个User,一套完整的调用链大概如下图所示:
以上是一个经过简化版的调用链,实际的调用链还要更复杂,调用过程中可能还包含了多个不同的扩展点(Extension)。
查询
假设我们存在一个名叫组织的实体Organization,我们想实现一个根据主键查询的功能,按照COLA的规范,我们应该现在client中创建对应的接口,比如是OrganizationResource
client:
public interface OrganizationResource {
/**
* 根据id查找
* @param id
* @return
*/
@GetMapping(value = "/org/{id}")
DataResponse<OrganizationCO> get(@PathVariable(value = "id")String id);
}
application:
@RestController
public class OrganizationController implements OrganizationResource {
/**
* 根据id查找
* @param id
* @return
*/
@Override
public DataResponse<OrganizationCO> get(String id) {
// 不再使用传统的service做业务查询,将查询的逻辑封装到了对应的领域对象中
OrganizationE org = OrganizationE.of().id(id);
return org != null ? DataResponse.of(org.convertToClient())
: DataResponse.buildFailure(BasicCode.DB_QUERY_NO_RESULT);
}
}
domain:
public class OrganizationE {
public static OrganizationE of() {
return new OrganizationE();
}
public OrganizationE id(String id) {
return getOrganizationGateway().getById(id);
}
/**
* 从对应的gateway中获取实现
**/
private OrganizationGateway getOrganizationGateway() {
if(this.organizationGateway == null) {
this.organizationGateway = SpringContextUtil.getBean(OrganizationGateway.class);
}
return this.organizationGateway;
}
}
repostitory:
@Component
public class OrganizationRepository extends RepositorySupport<OrganizationMapper, OrganizationDO> implements OrganizationGateway {
public OrganizationE getById(String id) {
OrganizationDO dataObject = super.getById(id);
return BeanMapper.INSTANCE.copy(dataObject, OrganizationE.class);
}
}
新增
client:
创建对应的command
注意:command类必须继承cola提供的Command,否则CommandBus无法发送这些消息给对应的Service处理
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class OrganizationCreateCmd extends Command {
/**
* 名称
*/
@NotBlank
private String name;
/**
* 编号,如果没指定,会根据不同的名称生成不同的编号,比如liSi会生成1001,zhangSan会生成2001
*/
private String code;
}
application:
@RestController
public class OrganizationController implements OrganizationResource {
@Autowired
private OrganizationService orgService;
/**
* 自定义创建cust1
* @return
*/
@PostMapping(value = "/org/cust1/create")
Response createCust1(@RequestBody OrganizationCreateCmd cmd) {
cmd.setBizScenario(BizScenario.valueOf(BizScenarioConstant.BIZ_ID.CUST1));
return orgService.create(cmd);
}
/**
* 自定义创建cust2
* @return
*/
@PostMapping(value = "/org/cust2/create")
Response createCust2(@RequestBody OrganizationCreateCmd cmd) {
cmd.setBizScenario(BizScenario.valueOf(BizScenarioConstant.BIZ_ID.CUST2));
return orgService.create(cmd);
}
}
service
@Service
public class OrganizationService {
// 注入COLA提供的command bus
@Autowired
private CommandBusI cmdBus;
/**
* 创建组织
* 有两个不同的扩展实现,分别会创建编号为1001和2001的组织信息
* @param cmd
* @return
*/
public Response create(OrganizationCreateCmd cmd) {
return cmdBus.send(cmd);
}
}
extension(扩展点)
public interface OrganizationCreateExtPt extends ExtensionPointI {
/**
* 组织创建的扩展点
* @param cmd
* @return
*/
OrganizationE extension(OrganizationCreateCmd cmd);
}
扩展点实现
@Extension(bizId = BizScenarioConstant.BIZ_ID.CUST1)
public class Cust1OrganizationCreateExt implements OrganizationCreateExtPt {
/**
* 组织创建的扩展点,根据不同的请求赋予不同的编号
* @param cmd
* @return
*/
@Override
public OrganizationE extension(OrganizationCreateCmd cmd) {
// 如果是cust1 赋予1001
return OrganizationE.of(cmd).setCode("1001");
}
}
@Extension(bizId = BizScenarioConstant.BIZ_ID.CUST2)
public class Cust2OrganizationCreateExt implements OrganizationCreateExtPt {
/**
* 组织创建的扩展点,根据不同的请求赋予不同的编号
* @param cmd
* @return
*/
@Override
public OrganizationE extension(OrganizationCreateCmd cmd) {
// 如果是cust2 赋予2001
return OrganizationE.of(cmd).setCode("2001");
}
}
扩展点执行器
@Command
public class OrganizationCreateCmdExe implements CommandExecutorI<Response, OrganizationCreateCmd> {
/**
* 参数校验器
*/
@Autowired
private OrganizationCreateValidator validator;
/**
* 扩展执行器
*/
@Autowired
private ExtensionExecutor executor;
@Override
public Response execute(OrganizationCreateCmd cmd) {
// 执行校验扩展
executor.executeVoid(OrganizationCreateValidatorExtPt.class,
cmd.getBizScenario(), exe-> exe.validate(cmd));
// 执行扩展逻辑
OrganizationE org = executor.execute(OrganizationCreateExtPt.class,
cmd.getBizScenario(), exe -> exe.extension(cmd));
// 调用领域对象的create方法
org.create();
return Response.buildSuccess();
}
}
domain
public class OrganizationE {
public OrganizationE create() {
if(!getOrganizationGateway().create(this)) {
throw new DBSaveFailedException();
}
return this;
}
}
repository
@Component
public class OrganizationRepository extends RepositorySupport<OrganizationMapper, OrganizationDO> implements OrganizationGateway {
@Transactional
public boolean create(OrganizationE organization) {
OrganizationDO dataObject = BeanMapper.INSTANCE.copy(organization, OrganizationDO.class);
return super.save(dataObject);
}
}