Applying Domain-Driven Design and Hexagonal Architecture in Ctrip International Train Ticket Booking System
This article presents a practical implementation of Domain-Driven Design (DDD) and hexagonal architecture in Ctrip's international train ticket booking platform, detailing strategic and tactical design, addressing controller bloat, over‑coupling, and anemic models, and showcasing Java code examples for aggregates, repositories, and factories.
Domain-Driven Design (DDD) is introduced as a software development philosophy that centers the domain, separating strategic design (domain, sub‑domain, bounded context) from tactical design (entity, value object, aggregate, factory, repository, domain events). The article applies DDD to Ctrip's international train ticket middle‑platform booking system.
Practice Background : The order creation process is illustrated with a service diagram and pseudocode, highlighting issues such as controller bloat, excessive coupling, and the anemic (bloodless) model where business logic resides outside domain objects.
Problems Identified : Controller layer becomes overly large as business logic grows. Modules become tightly coupled, forming a "Big Ball of Mud". Domain objects are merely data carriers without behavior (anemic model).
DDD Design : Strategic Design defines a ubiquitous language, domains, sub‑domains, and bounded contexts, illustrated with diagrams. Tactical Design introduces entities (e.g., Order), value objects (e.g., OrderSegment), aggregates (e.g., P2pOrder), and repositories to separate domain logic from persistence.
Hexagonal Architecture replaces traditional MVC layers with a six‑layer model: gateway, infrastructure, application, domain, plus adapters for external systems. This architecture follows the Dependency Inversion Principle, isolating business logic from technical concerns.
Implementation Examples :
@Override
protected CreateOrderResponse execute(CreateOrderRequest request) {
// 1. Parameter validation
if (!validate(request)) {
throw new BusinessException(P2pBookingResultCode.PARAM);
}
if (orderMapper.select(request.getOrderId()) != null) {
throw new BusinessException(P2pBookingResultCode.ORDER_EXISTS);
}
// 2. Initialize order
OrderDao orderDao = new OrderDao();
orderDao.setOrderId(request.getOrderId());
orderDao.setOrderStatus(100);
orderMapper.insert(orderDao);
// ... (additional steps: insurance, exchange rate, supplier order, etc.)
// 8. Return result
return mappingResponse(orderDao, orderInsuranceEntity, exchangeRateResponse);
} public class CreateOrderRequest extends CommonRequest {
private List
outSolutionOfferPairList;
private List
returnSolutionOfferPairList;
private String transactionNo;
// ... other fields ...
@Override
public void requestCheck() {
if (StringUtils.isEmpty(splitPlanId) && CollectionUtil.isEmpty(outSolutionOfferPairList)) {
throw new BusinessException(ResponseCodeEnum.PARAM_ERROR);
}
// ... additional checks ...
}
} public class P2pOrder {
private P2pOrderRepository repository;
@Getter private long orderId;
@Getter private OrderMasterModel orderMasterModel;
@Getter private List
orderItemModels;
public P2pOrder(P2pOrderRepository repository, long orderId) {
this.repository = repository;
this.orderId = orderId;
orderInfoModel = new OrderInfoModel();
orderItemModels = new ArrayList<>();
}
public boolean find() { return repository.find(this); }
public void createOrder(CreateOrderRequest request) {
if (find()) { throw new BusinessException(ResponseCodeEnum.ORDER_EXISTED); }
this.orderMasterModel.createOrderMaster(request);
repository.createP2pOrder(this);
pushDelayMessage(this);
}
}The article also describes the anti‑corruption layer that adapts external services (e.g., exchange rate, insurance) into internal models, and shows a factory class that generates order IDs and aggregates.
Service Structure : A diagram (hexagonal architecture) demonstrates how gateway, infrastructure, application, and domain layers interact, emphasizing separation of concerns and ease of maintenance.
Conclusion : By applying DDD and hexagonal architecture, the system achieves high cohesion, low coupling, clear domain boundaries, and easier evolution, while the anti‑corruption layer protects the core domain from external changes.
Ctrip Technology
Official Ctrip Technology account, sharing and discussing growth.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.