Modeling Domain‑Driven Design with ContextMapper DSL: From Subdomains to Bounded Contexts
This article explains how to use ContextMapper's DSL to express Domain‑Driven Design concepts—such as strategic and tactical design, subdomains, bounded contexts, context maps, user stories, and use cases—through concrete code examples, illustrating the benefits for architecture standardization and microservice integration.
Compared with traditional MVC, Domain‑Driven Design (DDD) is more abstract and harder to understand, leading to inconsistent documentation; the article proposes using a "DDD as Code" approach with a DSL to unify design thinking.
It introduces the distinction between tactical DDD (entities, value objects, aggregates, services, factories, repositories) and strategic DDD (bounded contexts, context maps, shared kernels, etc.), and explains how ContextMapper provides DSL support for both.
Subdomains are classified as Generic, Supporting, or Core, with examples of a membership system where domains like Account, UserTag, PaymentProfile, SnsProfile, and Profiles are defined. The following DSL snippet models the overall domain:
Domain User {
domainVisionStatement = "User domain to manage account, tags, profiles and payment profile."
Subdomain AccountDomain {
type = CORE_DOMAIN
domainVisionStatement = "Account domain to save sensitive data and authentication"
}
Subdomain UserTagDomain {
type = GENERIC_SUBDOMAIN
domainVisionStatement = "UserTag domain manage user's KV and Boolean tag"
}
Subdomain PaymentProfileDomain {
type = CORE_DOMAIN
domainVisionStatement = "User payment profile domain to manage credit/debit card, Alipay payment information"
}
Subdomain SnsProfileDomain {
type = CORE_DOMAIN
domainVisionStatement = "User Sns profile domain to manage user Sns profile for Weibo, Wechat, Facebook and Twitter."
}
Subdomain ProfilesDomain {
type = CORE_DOMAIN
domainVisionStatement = "User profiles domain to manage user basic profile, interest profile etc"
}
}For each subdomain, detailed DSL can describe entities and services, e.g., the Account subdomain:
Subdomain AccountDomain {
type = CORE_DOMAIN
domainVisionStatement = "Account domain to save sensitive data and authentication"
Entity Account {
long id
String nick
String mobile
String ^email
String name
String salt
String passwd
int status
Date createdAt
Date updatedAt
}
Service AccountService {
void updatePassword(long accountId, String oldPassword, String newPassword);
void resetPassword(long acountId);
boolean authByEmail(String email, String password);
boolean authBySmsCode(String mobile, String code);
}
}The ContextMap DSL describes relationships between bounded contexts, using notations like partnership, shared kernel, and anticorruption layer. An example ContextMap definition:
ContextMap UserContextMap {
type = SYSTEM_LANDSCAPE
state = TO_BE
contains AccountContext
contains UserTagContext
contains PaymentProfileContext
contains SnsProfileContext
contains ProfilesContext
contains UserLoginContext
contains UserRegistrationContext
UserLoginContext [D]<-[U] AccountContext {
implementationTechnology = "RSocket"
exposedAggregates = AccountFacadeAggregate
}
ProfilesContext [D]<-[U] UserTagContext {
implementationTechnology = "RSocket"
exposedAggregates = UserTags
}
UserRegistrationContext [D,C]<-[U,S] UserTagContext {
implementationTechnology = "RSocket"
exposedAggregates = UserTags
}
UserRegistrationContext [D,C]<-[U,S] SnsProfileContext {
implementationTechnology = "RSocket"
}
}BoundedContext definitions include type, vision statements, implementation technology, responsibilities, and aggregates. Example for the Account context:
BoundedContext AccountContext implements AccountDomain {
type = APPLICATION
domainVisionStatement = "Managing account basic data"
implementationTechnology = "Kotlin, Spring Boot, MySQL, Memcached"
responsibilities = "Account", "Authentication"
Aggregate AccountFacadeAggregate {
ValueObject AccountDTO {
long id
String nick
String name
int status
Date createdAt
def toJson();
}
Service AccountFacade {
@AccountDTO findById(Integer id);
}
}
Aggregate Accounts {
Entity Account {
long id
String nick
String mobile
String ^email
String name
String salt
String passwd
int status
Date createdAt
Date updatedAt
}
}
}The article also shows DSL for UserStory and UseCase, demonstrating how to capture requirements in a structured way.
UserStory Customers {
As a "Login User"
I want to update a "Avatar"
I want to update an "Address"
so that "I can manage the personal data."
} UseCase UC1_Example {
actor = "Insurance Employee"
interactions = create a "Customer", update a "Customer", "offer" a "Contract"
benefit = "I am able to manage the customers data and offer them insurance contracts."
}Benefits of using the DSL include clear, unambiguous architecture documentation, support for code generators, and easy integration with tools like PlantUML. The article discusses the relationship between DDD and microservices, emphasizing that DDD provides the conceptual foundation for service boundaries while microservice communication can be realized with REST, gRPC, or event‑driven messaging.
In conclusion, ContextMapper's DSL helps reduce ambiguity in DDD modeling, lowers the entry barrier for newcomers, and enables automated generation of documentation and code artifacts, though practical effectiveness still depends on real‑world adoption.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Qunar Tech Salon
Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.
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.
