Skip to main content الرئيسية المنشئون konglong87 methodology-skills ddd-tactical-design
ddd-tactical-design Use when implementing domain logic, designing aggregates, entities, value objects, repositories, domain services, domain model design, ensuring data consistency, or when user mentions 'aggregate design', 'aggregate root', 'entity vs value object', 'domain event', 'repository pattern', 'domain service', 'data consistency', 'invariant protection', 'domain model', 'business rules', 'consistency boundary', 'rich domain model', 'anemic domain model', '领域模型', '领域逻辑', '业务逻辑', '聚合设计', '聚合根', '实体', '值对象', '业务规则', '一致性边界', '充血模型', '贫血模型', '实现领域代码'.
الانتقال إلى التثبيت سوق المهارات اكتشف واستكشف مهارات الذكاء الاصطناعي التي بناها المجتمع.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
نسخ Promptعرض تفاصيل Prompt يتجاوز الأمر المباشر Prompt المخصّص للمراجعة. افحص المصدر قبل تشغيله.
npx skills add https://github.com/konglong87/methodology-skills --skill ddd-tactical-designيبقى الأمر في سطر واحد. مرّر أفقيًا لمراجعته كاملًا قبل النسخ.
تفضّل نسخة محلية؟ نزّل الملفات المتاحة حاليًا لدى SkillsMP.
تحميل Zip جاري التحميل... المهن ذات الصلة SOC
استنادا إلى تصنيف SOC المهني
name ddd-tactical-design version 2.0.0 description Use when implementing domain logic, designing aggregates, entities, value objects, repositories, domain services, domain model design, ensuring data consistency, or when user mentions 'aggregate design', 'aggregate root', 'entity vs value object', 'domain event', 'repository pattern', 'domain service', 'data consistency', 'invariant protection', 'domain model', 'business rules', 'consistency boundary', 'rich domain model', 'anemic domain model', '领域模型', '领域逻辑', '业务逻辑', '聚合设计', '聚合根', '实体', '值对象', '业务规则', '一致性边界', '充血模型', '贫血模型', '实现领域代码'. category design complexity high typical_duration 1hour dependencies [] benefits-from ["ddd-strategic-design"] conflicts-with [] output_artifact memory/artifacts/ddd-tactical-design/ allowed-tools ["Read","Write","Edit","Bash","Grep","Glob"] tags ["领域建模","代码实现","DDD","业务逻辑"]
Domain-Driven Design: Tactical Design
前置协议
环境检测
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "unknown" )
BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown" )
COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown" )
echo "PROJECT: $PROJECT_ROOT "
echo "BRANCH: $BRANCH "
echo "COMMIT: $COMMIT "
前置技能检查
benefits-from 检查 :
STRATEGIC_ARTIFACT="memory/artifacts/ddd-strategic/latest.json"
if [ -f "$STRATEGIC_ARTIFACT " ]; then
echo "FOUND: ddd-strategic-design artifact"
else
echo "INFO: No ddd-strategic-design artifact found"
echo "Consider running /ddd-strategic-design first to identify bounded contexts"
fi
工件目录初始化 :
mkdir -p memory/artifacts/ddd-tactical
Domain-Driven Design: Tactical Design
Overview
Tactical design provides building blocks for implementing domain models within a bounded context. These patterns help translate domain concepts into code that protects business rules and ensures data consistency.
Core building blocks:
Aggregate : Consistency boundary with a root entity
Entity : Object defined by identity, not attributes
Value Object : Immutable object defined by attributes
Domain Service : Operations that don't belong to entities
Repository : Persistence abstraction (collection-like)
Domain Event : Something happened in the domainWhy it matters: Without these patterns, business logic leaks into application services, databases, or UI. The domain model becomes anemic (just data holders).
Core principle: Protect invariants (business rules) and ensure consistency within aggregate boundaries.
Tactical design operates within bounded contexts defined by strategic design. If you haven't identified your bounded contexts yet, see ddd-strategic-design skill first.
When to Use
Implementing complex domain logic
Business rules need to be protected from corruption
Consistency boundaries matter (prevent invalid state)
Domain experts validate the model
Simple CRUD operations (no business rules)
Anemic domain model is acceptable (just data access)
Business logic lives in stored procedures or services only
The Process digraph tactical_design {
rankdir=TB;
"Identify Aggregates" [shape=box, style=filled, fillcolor="#c8e6c9"];
"Design Entities & Value Objects" [shape=box, style=filled, fillcolor="#bbdefb"];
"Define Domain Services" [shape=box, style=filled, fillcolor="#fff9c4"];
"Implement Repositories" [shape=box, style=filled, fillcolor="#e1bee7"];
"Publish Domain Events" [shape=box, style=filled, fillcolor="#f8bbd0"];
"Identify Aggregates" -> "Design Entities & Value Objects";
"Design Entities & Value Objects" -> "Define Domain Services";
"Define Domain Services" -> "Implement Repositories";
"Implement Repositories" -> "Publish Domain Events";
}
Step 1: Identify Aggregates An aggregate is a cluster of domain objects that can be treated as a single unit. It has:
Aggregate Root : The only entry point to the aggregate
Boundary : What's inside vs outside
Invariant : Business rule that must always be true
Aggregate: Order
├── Root: Order (entity)
├── Inside: OrderItems (entities), ShippingAddress (value object)
├── Outside: Customer (different aggregate), Product (different aggregate)
└── Invariant: Order total = sum of item prices (must always be true)
Only aggregate root has global identity
Objects inside accessed only through root
Root ensures invariants are maintained
Delete root → delete everything inside
Look for "transactional consistency" boundaries
What must be consistent after each operation?
What can be eventually consistent?
Keep aggregates small. Large aggregates cause performance issues and limit scalability.
Step 2: Design Entities & Value Objects
Has identity (ID) that persists through lifecycle
Mutable: state changes over time
Equality based on identity, not attributes
class Order {
private id : OrderId ;
private items : OrderItem [];
private status : OrderStatus ;
constructor (id : OrderId ) {
this .id = id;
this .items = [];
this .status = OrderStatus .Draft ;
}
addItem (product : Product , quantity : number ): void {
if (this .status !== OrderStatus .Draft ) {
throw new Error ("Cannot modify submitted order" );
}
this .items .push (new OrderItem (product, quantity));
}
equals (other : Order ): boolean {
return this .id .equals (other.id );
}
}
No identity (defined entirely by attributes)
Immutable: never changes after creation
Equality based on attribute values
class Money {
constructor (
readonly amount : number ,
readonly currency : string
) {
if (amount < 0 ) throw new Error ("Money cannot be negative" );
}
add (other : Money ): Money {
if (this .currency !== other.currency ) {
throw new Error ("Cannot add different currencies" );
}
return new Money (this .amount + other.amount , this .currency );
}
equals (other : Money ): boolean {
return this .amount === other.amount &&
this .currency === other.currency ;
}
}
Entity: Identity matters (User, Order, Account)
Value Object: Identity doesn't matter (Address, DateRange, Money)
Prefer Value Objects. They're simpler, immutable, and easier to test.
Step 3: Define Domain Services Some operations don't naturally fit in entities or value objects.
Involves multiple aggregates
No state of its own (stateless)
Named after domain activity (verb)
class TransferService {
transfer (
from : Account ,
to : Account ,
amount : Money
): void {
if (from .balance ().lessThan (amount)) {
throw new Error ("Insufficient funds" );
}
from .debit (amount);
to.credit (amount);
}
}
Avoid "Service" for everything. Put logic in entities first. Service is a fallback.
Step 4: Implement Repositories A repository provides collection-like interface for retrieving and storing aggregates.
Purpose: Abstract away persistence details. Domain model shouldn't care about database.
interface OrderRepository {
findById (id : OrderId ): Promise <Order | null >;
save (order : Order ): Promise <void >;
delete (order : Order ): Promise <void >;
findByCustomer (customerId : CustomerId ): Promise <Order []>;
findPendingOrders (): Promise <Order []>;
}
One repository per aggregate root
Repository works with aggregate roots only
Implementation deals with database, domain model doesn't
Step 5: Publish Domain Events A domain event represents something that happened in the domain that other parts of the system care about.
Named in past tense: OrderPlaced, PaymentProcessed
Immutable (it happened, can't change history)
Carries data needed by subscribers
class OrderPlaced {
constructor (
readonly orderId : OrderId ,
readonly customerId : CustomerId ,
readonly total : Money ,
readonly occurredAt : Date
) {}
}
place (): OrderPlaced {
if (this .items .length === 0 ) {
throw new Error ("Cannot place empty order" );
}
this .status = OrderStatus .Placed ;
return new OrderPlaced (this .id , this .customerId , this .total (), new Date ());
}
Use events to decouple aggregates. Aggregate A publishes event, Aggregate B subscribes. No direct coupling.
Building Blocks Reference Building Block Purpose Key Characteristic Entity Identity-based object Has ID, mutable, equality by ID Value Object Attribute-based object No ID, immutable, equality by value Aggregate Consistency boundary Root + entities + invariants Repository Persistence abstraction Collection-like interface Domain Service Cross-aggregate operation Stateless, verb-named Domain Event Something happened Past tense, immutable, carries data
Examples
Case 1: Order Aggregate Scenario: E-commerce order with items and shipping address.
class Money {
constructor (readonly amount : number , readonly currency : string ) {}
add (other : Money ): Money {
if (this .currency !== other.currency ) throw new Error ("Currency mismatch" );
return new Money (this .amount + other.amount , this .currency );
}
}
class Address {
constructor (
readonly street : string ,
readonly city : string ,
readonly zipCode : string
) {}
}
class OrderItem {
constructor (
readonly productId : string ,
readonly productName : string ,
readonly price : Money ,
readonly quantity : number
) {}
lineTotal (): Money {
return new Money (
this .price .amount * this .quantity ,
this .price .currency
);
}
}
class Order {
private items : OrderItem [] = [];
private status : OrderStatus = OrderStatus .Draft ;
constructor (
readonly id : string ,
readonly customerId : string ,
private shippingAddress : Address
) {}
addItem (product : Product , quantity : number ): void {
if (this .status !== OrderStatus .Draft ) {
throw new Error ("Cannot modify placed order" );
}
this .items .push (new OrderItem (
product.id ,
product.name ,
product.price ,
quantity
));
}
placeOrder (): OrderPlaced {
if (this .items .length === 0 ) {
throw new Error ("Cannot place empty order" );
}
this .status = OrderStatus .Placed ;
return new OrderPlaced (this .id , this .customerId , this .total ());
}
total (): Money {
return this .items .reduce (
(sum, item ) => sum.add (item.lineTotal ()),
new Money (0 , "USD" )
);
}
}
Case 2: Money Transfer Scenario: Transfer money between two accounts.
class Account {
constructor (
readonly id : string ,
private balance : Money
) {}
debit (amount : Money ): void {
if (this .balance .lessThan (amount)) {
throw new Error ("Insufficient funds" );
}
this .balance = this .balance .subtract (amount);
}
credit (amount : Money ): void {
this .balance = this .balance .add (amount);
}
currentBalance (): Money {
return this .balance ;
}
}
class TransferService {
transfer (from : Account , to : Account , amount : Money ): MoneyTransferred {
from .debit (amount);
to.credit (amount);
return new MoneyTransferred (from .id , to.id , amount);
}
}
class MoneyTransferred {
constructor (
readonly fromAccountId : string ,
readonly toAccountId : string ,
readonly amount : Money ,
readonly occurredAt : Date = new Date ()
) {}
}
Common Pitfalls
Pitfall 1: Large Aggregates Mistake: Putting too much inside one aggregate.
Example: Order aggregate includes Customer, Product, Payment, Shipment.
Performance: Loading huge object graph
Concurrency: Lock contention
Scalability: Cannot scale parts independently
Solution: Keep aggregates small. Only include what must be transactionally consistent.
Rule: If you don't need to load it to enforce an invariant, it's outside the aggregate.
Pitfall 2: Anemic Domain Model Mistake: Entities are just data holders. Logic lives in services.
class Order {
id : string ;
items : OrderItem [];
status : string ;
}
class OrderService {
addItem (order : Order , item : OrderItem ): void {
order.items .push (item);
}
}
Problem: Business rules scattered, easy to bypass invariants.
Solution: Put logic in entities. Services coordinate, entities enforce rules.
class Order {
addItem (item : OrderItem ): void {
if (this .status !== "Draft" ) throw new Error ("Cannot modify" );
this .items .push (item);
this .recalculateTotal ();
}
}
Pitfall 3: Business Logic in Services Mistake: Domain service becomes a god object with all business logic.
Problem: Entities become passive, invariants not protected, hard to test.
Try entity or value object first
Try aggregate root
Only then use domain service
Keep service focused: one operation, clear purpose
References
Domain-Driven Design by Eric Evans - The original DDD book, Part II: Tactical Design
Implementing Domain-Driven Design by Vaughn Vernon - Detailed patterns and examples
Domain-Driven Design Distilled by Vaughn Vernon - Concise tactical patterns
后置协议
工件输出
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
ARTIFACT_FILE="memory/artifacts/ddd-tactical/result-$TIMESTAMP .json"
cat > "$ARTIFACT_FILE " <<EOF
{
"skill": "ddd-tactical-design",
"version": "2.0.0",
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"project": "$PROJECT_ROOT",
"branch": "$BRANCH",
"commit": "$COMMIT",
"input": {
"user_request": "用户的原始请求"
},
"output": {
"aggregates": [
{
"name": "聚合名称",
"root": "聚合根实体",
"entities": ["Entity1", "Entity2"],
"value_objects": ["VO1", "VO2"],
"invariants": ["不变量1", "不变量2"]
}
],
"repositories": [
{
"name": "Repository名称",
"aggregate": "对应的聚合",
"methods": ["findById", "save", "delete"]
}
],
"domain_services": [
{
"name": "Domain Service名称",
"responsibility": "职责说明"
}
]
},
"next_skills": [
"mvp-first",
"pdca-cycle"
]
}
EOF
echo "ARTIFACT SAVED: $ARTIFACT_FILE "
ln -sf "$ARTIFACT_FILE " memory/artifacts/ddd-tactical/latest.json
目标文件更新
建议后续技能 ## 后续建议
基于 DDD 战术设计结果,建议继续执行:
**推荐技能链** :
1. /mvp-first - 进行 MVP 功能筛选和优先级规划
2. /pdca-cycle - 进入 PDCA 循环实施阶段
是否继续执行?
- A) 执行推荐的技能链
- B) 只执行第一个技能
- C) 不继续,结束当前任务