Applying Domain-Driven Design (DDD) to a Live E‑Commerce Service: Concepts, Architecture, and Practice
This article explains how a WeChat team used Domain‑Driven Design to improve the maintainability, scalability, and stability of a fast‑paced, multi‑team e‑commerce project, covering strategic and tactical modeling, layered architecture, subdomains, bounded contexts, anti‑corruption layers, domain events, aggregates, repositories, and a practical code scaffold with C++ examples.
The author describes challenges encountered while maintaining a high‑traffic, three‑tier e‑commerce system at Tencent, such as tangled RPC logic, oversized microservices, and tight coupling between business teams.
To address these issues, the team adopted Domain‑Driven Design (DDD), a methodology introduced by Eric Evans and popularized by Martin Fowler’s microservices paper. DDD replaces the traditional three‑layer model with a four‑layer architecture: UI, Application, Domain, and Infrastructure.
Layer responsibilities:
UI layer – protocol conversion, authentication, rate limiting, caching, and exception handling.
Application layer – orchestrates business processes (no business logic) and handles DTO conversion.
Domain layer – defines domain models, services, repositories, and anti‑corruption interfaces.
Infrastructure layer – implements repositories, anti‑corruption layers, and provides storage capabilities.
The design emphasizes Dependency Inversion (DIP) so that high‑level modules depend on abstractions, not concrete implementations, illustrated by the onion architecture diagram.
Strategic and tactical modeling: Strategic modeling splits the problem space into subdomains (core, generic, supporting) and defines bounded contexts. Tactical modeling maps each bounded context to a microservice, creates domain services, aggregates, and entities, and uses anti‑corruption layers to isolate contexts.
Key concepts such as bounded context , ubiquitous language , domain events , entities , value objects , aggregates , DTOs , and repositories are explained with concrete examples from the product domain.
Example code shows how a domain object defines conversion methods and how the application layer transforms a protobuf DTO into a domain entity before persisting it via a repository:
class DetailRecord {
public:
int ConvertFromDTO(const google::protobuf::Message& oDto);
int ConvertToDO(detailrecordinfrastructure::DetailRecordDO& oDo);
/*...*/
};
int DetailrecordApplication::InsertDetailRecord(unsigned int head_uin, const InsertDetailRecordReq& req, InsertDetailRecordResp* resp) {
DetailRecord oRecord;
int iRet = oRecord.ConvertFromDTO(req);
iRet = m_oDetailRecordGateway->Save(oRecord);
return iRet;
}
int DetailRecordGatewayImpl::Save(DetailRecord& oEntity) {
detailrecordinfrastructure::DetailRecordDO oDo;
int iRet = oEntity.ConvertToDO(oDo);
iRet = oKvMapper.insert(oDo);
iRet = oEventMapper.publish(oDo);
return iRet;
}The article also discusses using CQRS to keep simple queries lightweight, avoiding excessive DTO‑to‑domain‑to‑data‑object transformations.
Finally, a directory‑level scaffolding is presented to help teams bootstrap a DDD‑based microservice, showing where adapters, application services, domain aggregates, gateways, infrastructure implementations, and protobuf definitions belong.
High Availability Architecture
Official account for High Availability Architecture.
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.