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);
}
}