作者简介
Ma Ning,携程国际火车票后端开发工程师,关注系统架构、微服务、高可用等技术领域。
一、前言
领域驱动设计(Domain-Driven Design,简称 DDD)是一种软件开发设计思想,其旨在以领域为核心,让软件系统在实现时准确地基于对真实业务过程的建模,专注于业务问题域的需要。
DDD将软件系统设计分为了2个部分:战略设计和战术设计,战略设计用于提炼问题域并塑造应用程序的架构,战术设计用于帮助创建用于复杂有界上下文的有效模型。基于此,DDD强调专注于核心领域,通过协作对公共语言和知识进行提炼,并且持续致力于领域的知识提炼,让模型持续发展。
本文基于DDD思想,在携程国际火车票中台预订系统项目进行实践。
二、实践背景
本文以国际火车票中台预订系统项目的创单流程为例,其服务结构下图所示:
伪代码如下所示:
@Override
protected CreateOrderResponse execute(CreateOrderRequest request) {
// 1、参数校验
if (!validate(request)) {
throw new BusinessException(P2pBookingResultCode.PARAM);
}
if (orderMapper.select(request.getOrderId()) != null) {
throw new BusinessException(P2pBookingResultCode.ORDER_EXISTS);
}
// 2、初始化订单
OrderDao orderDao = new OrderDao();
orderDao.setOrderId(request.getOrderId());
orderDao.setOrderStatus(100);
orderMapper.insert(orderDao);
// 初始化乘客信息
PassengerDao passengerDao = new PassengerDao();
...
passengerMapper.insert(passengerDao);
// 3、转换汇率
ExchangeRate exchangeRate = exchangeService.getExchangeRate(originCurrency, targetCurrency);
// 4、购买保险
if (isBuyInsurance(request)) {
// 调用保险服务
InsuranceInfo insuranceInfo = insuranceService.buyInsurance(request);
// 保存保险信息
InsuranceDao insuranceDao = new InsuranceDao();
...
insuranceMapper.insert(insuranceDao);
}
// 5、供应商创单
SupplierOrder supplierOrder = supplierService.createOrder(request, exchangeRate);
// 保存供应商订单信息
SupplierOrderDao supplierOrderDao = new SupplierOrderDao();
...
supplierOrderMapper.insert(SupplierOrderDao);
// 6、保存订单信息
orderDao = new orderDao();
orderDao.setOrderId(request.getOrderId);
orderDao.setOrderStatus(OrderStatusEnum.WAIT_FOR_PAY.getCode());
...
orderMapper.update(orderDao);
// 7、发送超时支付取消消息
messageProducer.push(MessageQueueConstants.TOPIC_TIMEOUT_CANCEL, "orderId", String.valueOf(orderDao.getOrderId()), appSettingProp.getTimeoutMinutes(), TimeUnit.MINUTES);
// 8、返回结果
return mappingResponse(orderDao, orderInsuranceEntity, exchangeRateResponse);
}
2.1 控制层臃肿
在传统的互联网软件架构中,通常都会采用MVC三层架构,其是一种古老且经典的软件设计模式,基于分层架构的思想,将整个程序分为了Model、View和Controller三层:
Model(模型层):最底下一层,是核心的数据,也就是程序需要操作的数据或信息;
View(视图层):最上面一层,直接面向最终用户的视图,它是提供给用户的操作界面,是程序的外壳;
Controller(控制层):中间的一层,就是整个程序的逻辑控制核心,它负责根据视图层输入的指令选取数据层的数据,然后对其进行相应操作产生最终结果;
MVC三层架构模式,将软件架构分为了三层,就可以让软件实现模块化,使三层相互独立,修改外观或者变更数据都不需要修改其他层,方便了维护和升级。但是这种软件架构中模型层只关注数据,控制层只关注行为,随着迭代的不断演化,业务逻辑越来越复杂,便会导致整个控制层的代码量越来越多,而模型层和视图层的变更却很少,最终导致整个控制层变得十分臃肿,从而失去了分层的意义。
2.2 过度耦合
在业务初期,程序的功能都非常简单,此时系统结构逻辑是清晰的,但是随着程序的不断迭代,一方面会导致业务逻辑越来越复杂,系统逐渐冗余,模块之间彼此关联,软件架构设计模式逐渐向“大泥球”模式(BBoM,Big Ball of Mud)发展;另一方面系统会调用越来越多的第三方服务,从而导致数据格式不兼容,业务逻辑无法复用。
在出票系统中,除了订单相关的功能外,还包括了保险、汇率、供应商订单