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.

Ctrip Technology
Ctrip Technology
Ctrip Technology
Applying Domain-Driven Design and Hexagonal Architecture in Ctrip International Train Ticket Booking System

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<SolutionOfferPair> outSolutionOfferPairList;
    private List<SolutionOfferPair> 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<OrderItemModel> 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaDDDHexagonal ArchitectureDomain Modeling
Ctrip Technology
Written by

Ctrip Technology

Official Ctrip Technology account, sharing and discussing growth.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.