我们已经基于COLA架构创建了装卸服务(handlingms)的骨架代码,然后创建好了领域模型和数据模型。本文将按照数据流的顺序,分别在各个模块的包结构中,实现业务逻辑和代码落地。


一、二方库client模块
二方库client模块不是功能独立的层,client模块包含的代码应该是常见的服务接口Facade和DTO数据传输对象,如API、DTO、领域事件(Domain Event)、Command和Query对象等等。

如上图所示,HandlingActivityRegisterCmd 类表示装卸活动注册命令,ICargoHandlingService 类定义包裹装卸服务API,将在app模块中实现该API服务。
public interface ICargoHandlingService {
Response registerHandlingActivity(HandlingActivityRegisterCmd cmd);
}
二、适配层(Adapter Layer)模块
适配层(Adapter Layer)负责进行路由和适配。Controller作为六边形架构中与HTTP端口的适配器,起到了适配请求,并委托应用层处理。

在适配层中创建CargoHandlingController控制器类,负责处理HandlingActivityRegisterCmd命令,代码如下所示:
@RestController
@RequestMapping("cargohandling")
public class CargoHandlingController {
@Autowired
private ICargoHandlingService cargoHandlingService;
@PostMapping
public SingleResponse<String> registerHandlingActivity(@RequestBody HandlingActivityRegisterCmd cmd){
System.out.println("装卸活动 预订号:" + cmd.getBookingId() +
" 类型:" + cmd.getHandlingType());
cargoHandlingService.registerHandlingActivity(cmd);
return SingleResponse.of("Handling Activity Registered");
}
}
在controller中,调用应用服务的registerHandlingActivity()方法,登记装卸活动。
三、应用层(Application Layer)模块
应用层(Application Layer)负责实现client模块中定义的API接口。在API接口的实现中,调用executor包中Command Handler或Query Handler内部的唯一execute()方法,该方法将协调领域模型和领域服务完成业务功能。

如上图所示,app模块中首先创建CargoHandlingServiceImpl实现类,实现在client模块中定义的ICargoHandlingService。在registerHandlingActivity方法中,调用handlingActivityRegisterCmdExe命令执行器中的execute()方法,并传入HandlingActivityRegisterCmd对象,代码如下所示:
@Service
public class CargoHandlingServiceImpl implements ICargoHandlingService {
@Autowired
private HandlingActivityRegisterCmdExe handlingActivityRegisterCmdExe;
public Response registerHandlingActivity(HandlingActivityRegisterCmd cmd){
return handlingActivityRegisterCmdExe.execute(cmd);
}
}
在handlingActivityRegisterCmdExe.execute()方法中,直接调用领域层中定义的gateway方法。通过引入gateway接口来隔离infrastructure模块中的技术实现细节。
@Component
public class HandlingActivityRegisterCmdExe {
@Autowired
private HandlingGateway handlingGateway;
public Response execute(HandlingActivityRegisterCmd cmd){
System.out.println("装卸航程号:" + cmd.getVoyageNumber());
HandlingActivity handlingActivity = new HandlingActivity(
EventType.valueOf(cmd.getHandlingType()),cmd.getLocation(),cmd.getCompletionTime(), cmd.getBookingId()
);
if(!cmd.getVoyageNumber().equals("")) {
handlingActivity.setVoyageNumber(cmd.getVoyageNumber());
}
handlingGateway.save(handlingActivity);
return Response.buildSuccess();
}
}
四、领域层(Domain Layer)模块
领域层(Domain Layer)包含了业务核心领域模型的代码,除了领域模型的定义之外,还有gateway接口的定义,如图所示。

通过网关接口,用来隔离技术实现,HandlingGateway接口定义如下所示,目前只定义了一个save方法,用来进行装卸活动(HandlingActivity)的持久化操作。
public interface HandlingGateway {
void save(HandlingActivity handlingActivity);
}
五、落地基础设施(Infrastructure Layer)模块
基础设施层的代码组织在infrastructure模块中。
基础设施可以对抽象的接口进行实现,上文中说到资源库Repository的Gateway接口定义在领域层,那么在基础设施中就可以具体实现这个接口。

HandlingGatewayImpl实现类代码如下所示:
@Repository
public class HandlingGatewayImpl implements HandlingGateway {
@Resource
private HandlingActivityMapper handlingActivityMapper;
@Override
public void save(HandlingActivity handlingActivity) {
HandlingActivityDO handlingActivityDO = HandlingActivityConvertor.toDataObject(handlingActivity);
handlingActivityMapper.insert(handlingActivityDO);
}
}
首先将handlingActivity领域对象转换为handlingActivityDO数据对象,然后通过MyBatis持久化框架完成数据存储操作。
现在,启动装卸服务应用,通过Postman发起POST请求,请求参考报文如下所示:
{"bookingId" : "0BC34F4A","location" : "CNHKG","handlingType" : "LOAD","completionTime": "2021-06-02","voyageNumber" : "0100S"}
如下图所示,Post请求执行完成,返回已经登记装卸活动,活动类型为:LOAD。

至此,我们基本完成了装卸微服务应用的基本功能-登记装卸活动。下一章,我们将继续完成该服务,实现领域事件CargoHandledEvent(包裹装卸事件)。
