
Ddd Architecture Awesome
- 13 installs
- 1 repo stars
- Updated July 29, 2026
- full-statck-skills/ddd-skills
Entry-point guide to Domain-Driven Design concepts, applicability assessment, complexity ladder, core patterns, and anti-patterns.
About
Serves as the entry point to DDD, explaining concepts, when DDD fits, the complexity ladder, and core Entity/ValueObject/Aggregate patterns. A developer uses it to understand DDD basics and decide whether to adopt it.
- DDD applicability assessment and complexity ladder
- Core concepts and anti-patterns with fixes
Ddd Architecture Awesome by the numbers
- 13 all-time installs (skills.sh)
- Ranked #3,516 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
npx skills add https://github.com/full-statck-skills/ddd-skills --skill ddd-architecture-awesomeAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 13 |
|---|---|
| repo stars | ★ 1 |
| Last updated | July 29, 2026 |
| Repository | full-statck-skills/ddd-skills ↗ |
What it does
Entry-point guide to Domain-Driven Design concepts, applicability assessment, complexity ladder, core patterns, and anti-patterns.
Files
DDD Architecture Awesome
The entry point to the DDD Skills ecosystem. This skill provides everything needed to understand DDD, decide if it's right for your project, and chart your learning path — all in one place.
When to Use (and When NOT to)
| ✅ Use When | ❌ Skip When |
|---|---|
| Complex business domain with many rules | Simple CRUD, few business rules |
| Long-lived system (years of maintenance) | Prototype, MVP, throwaway code |
| Team of 5+ developers | Solo developer or small team (1-2) |
| Multiple entry points (API, CLI, events) | Single entry point, simple API |
| Need to swap infrastructure (DB, broker) | Fixed infrastructure, unlikely to change |
| High test coverage required | Quick scripts, internal tools |
| Chinese enterprise Spring Boot + MyBatis stack | Already using established architecture that works |
Start simple. Evolve complexity only when needed. Most systems don't need full CQRS or Event Sourcing.
When to trigger this skill
ALWAYS use this skill when the user mentions:
- "What is DDD", "Explain DDD", "DDD 是什么", "介绍一下 DDD"
- "Domain-Driven Design", "领域驱动设计"
- "DDD basics", "DDD fundamentals", "DDD 基础", "DDD 入门"
- "Should I use DDD", "DDD 适用场景", "DDD 是否适合"
- "DDD vs CRUD", "DDD vs traditional", "DDD 对比"
- "DDD concepts", "DDD 概念"
- "Strategic design DDD", "Tactical design DDD", "DDD 战略设计", "DDD 战术设计"
- "When to apply DDD", "When DDD is appropriate"
- Questions about DDD architecture types and their differences
- New to DDD and seeking guidance on where to start
---
1. DDD Definition
Domain-Driven Design is a development methodology that brings complex business rules into domain models: first define business boundaries (Strategic Design), then encode rules into models (Tactical Design).
Key insight: DDD is not about technology — it's about aligning your code with how the business thinks.
---
2. DDD Applicability Decision Tree
Evaluate along these dimensions and provide a "Suitable / Cautious / Not Recommended" conclusion:
| Dimension | Suitable (✓) | Cautious (~) | Not Recommended (✗) |
|---|---|---|---|
| Business Complexity | Complex rules, state machines, cross-team coordination | Some business rules, mostly simple | Pure CRUD, data entry |
| Lifecycle | Long-lived, continuous evolution | Medium-term, some iteration | Short-term, disposable |
| Team Maturity | Dedicated domain expert, stable PM | General business knowledge | No domain knowledge |
| Engineering Capability | Strong test/observability, event-driven ready | Basic CI/CD, some testing | No testing infrastructure |
Decision Flowchart
Is business logic complex (rules, state machines, constraints)?
├── NO → Is system long-lived (> 2 years)?
│ ├── NO → ✗ DDD NOT RECOMMENDED (Use simple CRUD + Service)
│ └── YES → ~ Cautious: adopt tactical patterns selectively
│
└── YES → Is there a domain expert or stable product owner?
├── NO → ~ Cautious: DDD without domain expert = complex code for nothing
└── YES → Is the team familiar with DDD or willing to learn?
├── NO → ~ Cautious: start with Layered, evolve later
└── YES → Do you need to swap infrastructure (DB, MQ)?
├── NO → ✓ DDD RECOMMENDED (COLA or Layered)
└── YES → ✓ DDD STRONGLY RECOMMENDED (Hexagonal/Clean)---
3. Complexity Ladder — Don't Skip Levels
Each level adds real complexity. Move up only when you've proven the current level insufficient.
Level 1: Simple Layered (Controller → Service → Repository)
↓ When business rules grow complex and need explicit modeling
Level 2: DDD 4-Layer with Rich Domain Model (Layered)
↓ When need multiple entry points (REST + CLI + MQ + gRPC)
Level 3: Ports & Adapters (Hexagonal Architecture)
↓ When read/write patterns diverge significantly
Level 4: CQRS — Separate Read/Write Models
↓ When need complete audit trail / temporal queries
Level 5: Event Sourcing — Store events, derive state
Your project Level: ___ (Recommend with reasoning)---
4. Pattern Boundaries — What Each Pattern IS and IS NOT
This table solves the most common DDD confusion: "What's the difference between these patterns, and when should I use each one?"
| Pattern | Primary Question | Use It For | Do NOT Treat As |
|---|---|---|---|
| DDD | How do we model a complex business domain? | Ubiquitous language, bounded contexts, aggregates, value objects | A folder structure by itself |
| Hexagonal Architecture | How does the application interact with the outside world? | Ports, driver adapters, driven adapters, testable application core | A mandate for six sides or one exact package layout |
| Clean Architecture | Which direction should dependencies point? | Inward dependency rule, use case boundaries, framework independence | A universal four-folder template |
| Onion Architecture | How do we keep the domain model central? | Domain-centered layers and dependency inversion | A separate requirement when Clean/Hexagonal already solve the problem |
| COLA v5 | How do we standardize DDD in enterprise Java teams? | Diamond architecture, scaffolding, automated architecture validation | A silver bullet for all projects |
| Layered Architecture | How do we introduce DDD incrementally? | 4-layer separation, minimal disruption to existing 3-layer projects | A permanent destination (evolve when ready) |
| CQRS | Do reads and writes need different models? | Bounded contexts with divergent read/write workloads | A default application architecture |
| Event Sourcing | Do we need state from a complete event history? | Audit trails, temporal queries, replayable workflows | A persistence default for CRUD systems |
---
5. DDD Architecture Landscape Overview
DDD Architecture Family:
Foundational (Learning cost: Low → Medium):
├── Layered Architecture (分层架构)
│ └── Classic 4-layer: Interface → Application → Domain ← Infrastructure
│ 1-5 people | CRUD-friendly | Spring Boot native
│
├── Onion Architecture (洋葱架构)
│ └── Domain core, concentric dependency rings, inner defines interface
│ 5-15 people | High testability | Frequent infra changes
│
├── Hexagonal Architecture (六边形架构 / Ports & Adapters)
│ └── Domain at center, ports define contracts, adapters implement
│ 5-15 people | Multi-entry systems | Best testability
│
Advanced (Learning cost: Medium → High):
├── Clean Architecture (整洁架构)
│ └── Entities → Use Cases → Interface Adapters → Frameworks
│ 15-50 people | Enterprise systems | Strict module isolation
│
└── COLA Architecture (COLA v5 架构)
└── Diamond pattern: Adapter → App → Domain ← Infrastructure
5-50 people | Chinese enterprise | Best tooling & community
Complementary Patterns:
├── CQRS — Separate read/write models (L1: Model only / L2: DB separation / L3: Event Sourcing)
├── Domain Events — Cross-aggregate eventual consistency
└── Event Sourcing — Store events as source of truthArchitecture Visual Comparison
Layered Onion Hexagonal Clean COLA v5
┌────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│Interf. │ │ Infra │ │ Adapter │ │ Frame. │ │ Adapter │
├────────┤ │ ┌──────┐ │ ├──────────┤ ├──────────┤ ├──────────┤
│ App │ │ │ App │ │ │ Ports │ │ Adapter │ │ App │
├────────┤ │ │┌────┐│ │ ├──────────┤ ├──────────┤ ├──────────┤
│ Domain │ │ ││Dom.││ │ │ Domain ★ │ │ Domain ★ │ │ Domain ★ │
├────────┤ │ ││ ★ ││ │ └──────────┘ └──────────┘ ├──────────┤
│ Infra │ │ │└────┘│ │ │ Infra │
└────────┘ │ └──────┘ │ └──────────┘
└──────────┘---
6. Core DDD Concepts Quick Reference
6.1 Strategic Design (战略设计)
| Concept | Definition | Key Point |
|---|---|---|
| Bounded Context | A boundary within which a domain model is consistent | Each context has its own ubiquitous language |
| Ubiquitous Language | A shared language between developers and domain experts | Used in code, conversations, and documentation |
| Context Mapping | Relationships between bounded contexts | Partnership, Shared Kernel, Customer-Supplier, ACL, OHS |
| Core Domain | The most important part of the business | Invest the most effort here |
| Subdomain | Supporting or generic business capabilities | Core / Supporting / Generic |
6.2 Tactical Design (战术设计)
| Pattern | Purpose | Key Rule |
|---|---|---|
| Entity | Object with identity that persists | Equality by ID, not attributes |
| Value Object | Immutable data defined by attributes | Equality by value, no setters |
| Aggregate | Consistency boundary | One aggregate = one transaction |
| Aggregate Root | Single entry point to aggregate | Only root referenced externally |
| Repository | Persistence abstraction | One repository per aggregate |
| Domain Service | Stateless cross-entity logic | When logic doesn't fit any entity |
| Domain Event | Record of meaningful change | Past tense naming (OrderPaid) |
| Factory | Complex object creation | When constructor isn't enough |
| Specification | Composable business rule | AND/OR/NOT combinable |
6.3 Quick Decision Trees
"Where does this code go?"
Where does this code go?
├─ Pure business logic, no I/O → domain/
├─ Orchestrates domain + has side effects → application/
├─ Talks to external systems → infrastructure/
├─ Defines HOW to interact (interface) → port (domain or application)
└─ Implements a port → adapter (infrastructure)"Entity or Value Object?"
Entity or Value Object?
├─ Has unique identity that persists → Entity
├─ Defined only by its attributes → Value Object
├─ "Is this THE same thing?" → Entity (identity comparison)
└─ "Does this have the same value?" → Value Object (structural equality)"Should this be its own Aggregate?"
Aggregate boundaries?
├─ Must be consistent together in a transaction → Same aggregate
├─ Can be eventually consistent → Separate aggregates
├─ Referenced by ID only → Separate aggregates
└─ >10 entities in aggregate → Split itRule: One aggregate per transaction. Cross-aggregate consistency via domain events (eventual consistency).
---
7. Rich Domain Model vs Anemic CRUD (Critical Distinction)
// ❌ Anemic Model (Anti-Pattern) — Data bag + Service
@Entity
public class Order {
private Long id;
private String status; // String instead of Value Object
// Only getters/setters, NO behavior
}
@Service
public class OrderService { // God Service with all logic
@Transactional
public void pay(Long orderId) {
Order order = orderRepo.findById(orderId);
if ("DRAFT".equals(order.getStatus())) { // Raw string comparison
order.setStatus("PAID");
orderRepo.save(order);
}
}
}
// ✅ Rich Domain Model (DDD) — Behavior WITH data
public class Order extends AggregateRoot<OrderId> {
private OrderStatus status; // Value Object
private Money totalAmount;
private List<OrderItem> items;
public void pay() { // Behavior in entity
if (!status.canPay()) {
throw new OrderException("Cannot pay in current status");
}
this.status = OrderStatus.PAID;
addDomainEvent(new OrderPaidEvent(this.id));
}
}Aggregate Sizing Heuristics
| Metric | Healthy | Warning | Action |
|---|---|---|---|
| Entities per aggregate | 1-5 | 6-10 | >10: Split |
| Lines of code (root) | <500 | 500-1000 | >1000: Split |
| Transaction lock time | <100ms | 100-500ms | >500ms: Split |
| Concurrent modification conflicts | Rare | Occasional | Frequent: Split |
---
8. Anti-Patterns (CRITICAL — with Fixes)
| Anti-Pattern | Problem | Fix |
|---|---|---|
| Anemic Domain Model | Entities are data bags, logic in services | Move behavior INTO entities |
| Repository per Table | Breaks aggregate boundaries | One repository per AGGREGATE |
| Leaking Infrastructure | Domain imports DB/HTTP libraries | Domain has ZERO external dependencies |
| God Aggregate | Too many entities, slow transactions | Split into smaller aggregates |
| Skipping Use Cases | Controllers call repositories directly | Route through application use cases |
| CRUD Thinking | Modeling data, not behavior | Model business operations |
| Premature CQRS | Adding complexity before needed | Start simple, evolve |
| Cross-Aggregate TX | Multiple aggregates in one transaction | Use domain events for consistency |
| DDD without Domain Expert | Architecture without business insight | DDD without experts = complex code |
| Framework-First Thinking | Choosing tech before understanding domain | Domain first, technology second |
---
9. Implementation Order (Universal)
1. Discover the Domain — Event Storming, conversations with domain experts
2. Model the Domain — Entities, value objects, aggregates (NO infrastructure)
3. Define Ports — Repository interfaces, external service interfaces
4. Implement Use Cases — Application services coordinating domain
5. Add Adapters LAST — HTTP, database, messaging implementations
DDD is collaborative. Modeling sessions with domain experts are as important as code patterns.---
10. How to Use This Skill
Step 1: Understand user context
Ask about: project nature, business complexity, team structure, current state.
Step 2: Assess DDD applicability
Run through the decision tree (Section 2) and output:
- Suitable: Proceed to architecture selection
- Cautious: Recommend selective adoption
- Not Recommended: Explain why, prevent over-engineering
Step 3: Recommend learning path
| User Type | Recommended Path |
|---|---|
| DDD Novice | awesome → selector → (architecture Skill) → code-reviewer |
| Architect/Tech Lead | selector → (architecture Skill) → domain-designer → doc |
| Migrating to DDD | awesome → selector → (architecture Skill) → reviewer → evaluator |
| Layered | selector → architecture-layered → domain-designer |
| Onion | selector → architecture-onion → domain-designer |
| Hexagonal | selector → architecture-hexagonal → domain-designer + api-designer |
| Clean | selector → architecture-clean → domain-designer |
| COLA | selector → architecture-cola → domain-designer → api-designer |
| Needs CQRS | selector → cqrs-architecture (standalone or embedded in architecture) |
| Microservices + Events | selector → (architecture Skill) → cqrs-architecture → api-designer |
| Code Review | code-reviewer (standalone) |
| Architecture Eval | evaluator (periodic) |
| Architecture Doc | doc (standalone, read existing code) |
| Event Storming Workshop | event-storming (standalone or paired with domain-designer) |
| Testing Strategy | testing-strategist (standalone or paired with architecture Skill) |
| DevOps Integration | devops-integration (paired with CI/CD pipeline) |
Step 4: Output format
Always structure response with: 1. 适用性评估 (Applicability Assessment) — clear conclusion + reasoning per dimension 2. DDD 核心概念速览 (Core Concepts Quick Reference) — tailored to context 3. 推荐学习路径 (Recommended Learning Path) — which skills, what order
---
11. Sources
Primary Sources — Original Papers & Books
- Domain-Driven Design: The Blue Book — Eric Evans (2003)
- Implementing Domain-Driven Design — Vaughn Vernon (2013)
- Hexagonal Architecture — Alistair Cockburn (2005)
- The Clean Architecture — Robert C. Martin (2012)
- Onion Architecture — Jeffrey Palermo (2008)
Primary Pattern References
- CQRS — Martin Fowler
- Event Sourcing — Martin Fowler
- Repository Pattern — Martin Fowler (PoEAA)
- Unit of Work — Martin Fowler (PoEAA)
- Bounded Context — Martin Fowler
- Effective Aggregate Design — Vaughn Vernon
Implementation Guides
- Microsoft: DDD + CQRS Microservices
- Domain Events – Salvation — Udi Dahan
- Transactional Outbox — microservices.io
- Introducing Event Storming — Alberto Brandolini
- C4 Model — Simon Brown
- Documenting Architecture Decisions — Michael Nygard
Supplemental Syntheses
- Clean Architecture: Standing on the Shoulders of Giants — Herberto Graça
- Explicit Architecture — Herberto Graça
- Get Your Hands Dirty on Clean Architecture — Tom Hombergs
- COLA 5.0 Architecture — Alibaba
Chinese Resources (中文资源)
- PartMe DDD 系列文章 — 基础篇/进阶篇/实战篇
- Spring AI 官方文档
Reference Implementations by Language
| Language | Repository | Architecture |
|---|---|---|
| Java | thombergs/buckpal | Clean Architecture |
| Java | alibaba/COLA | COLA v5 |
| Go | bxcodec/go-clean-arch | Clean Architecture |
| Rust | flosse/clean-architecture-with-rust | Clean Architecture |
| Python | cdddg/py-clean-arch | Clean Architecture |
| TypeScript | jbuget/nodejs-clean-architecture-app | Clean Architecture |
| .NET | jasontaylordev/CleanArchitecture | Clean Architecture |
---
Reference Library(按需加载)
This skill bundles a structured reference library organized by learning path. Each file is loaded only on demand — when the agent identifies the need.
How to use these references
When a user asks a question, find the matching category below and read the relevant file(s). Do NOT load all references at once.
| User asks about... | Load this file |
|---|---|
| "Why DDD?", "Should I use DDD?", "DDD vs CRUD" | references/01-strategic/01-why-ddd.md |
| "What is a domain / subdomain?", "Core vs Generic vs Supporting" | references/01-strategic/02-domain-and-subdomain.md |
| "What is bounded context?", "How to split bounded contexts?", "Context mapping" | references/01-strategic/03-bounded-context.md |
| "Entity vs Value Object", "When to use Entity / VO?", "Equality patterns" | references/02-tactical/01-entity-and-value-object.md |
| "How to design aggregates?", "Aggregate design principles", "Aggregate boundaries" | references/02-tactical/02-aggregate-design.md |
| "Deep dive: aggregate design process", "5 steps from event storming to aggregate" | references/02-tactical/03-aggregate-deep-dive.md |
| "Domain events explained", "How to use events for decoupling", "Event bus, Event Sourcing" | references/02-tactical/04-domain-events.md |
| "DDD building blocks", "Naming conventions", "Aggregate size metrics" | references/02-tactical/05-building-blocks.md |
| "All DDD tactical patterns explained", "Repository, Factory, Specification patterns" | references/02-tactical/06-tactical-concepts.md |
| "Clean Architecture tactical reference (original source)" | references/02-tactical/07-clean-tactical-reference.md |
| "Compare DDD architectures", "Which architecture to choose?", "Layered vs Hexagonal vs Clean" | references/03-architecture/01-comparison.md |
| "Is DDD right for my project?", "DDD applicability decision matrix", "Real-world scenarios" | references/03-architecture/02-decision-matrix.md |
| "DDD layered architecture in detail", "4-layer responsibilities, strict vs loose layering", "Three-tier to DDD migration" | references/03-architecture/03-layered-architecture-detail.md |
| "Architecture deep dive", "DDD + Hexagonal + Clean + COLA explained in depth" | references/03-architecture/04-architecture-deep-dive.md |
| "Four-layer architecture reference", "Domain/Application/Infrastructure/Presentation code templates" | references/03-architecture/05-clean-layers-reference.md |
| "Hexagonal architecture reference", "Ports & Adapters code examples" | references/03-architecture/06-clean-hexagonal-reference.md |
| "Strategic DDD reference (original source)", "Bounded contexts, context mapping" | references/03-architecture/07-clean-strategic-reference.md |
| "Quick cheatsheet", "Pattern boundaries, layer summary, decision trees" | references/04-quick-ref/01-cheatsheet.md |
| "DDD mindmap — complete knowledge system panorama", "Strategy, tactics, architecture, process, principles, implementation" | references/04-quick-ref/02-ddd-mindmap.md |
| "Event Storming workshop methodology", "6-step workshop process, facilitator guide", "How to run DDD discovery" | references/05-implementation/01-event-storming-workshop.md |
| "Domain-to-code object mapping", "How to map domain objects to code objects", "Mapping table template" | references/05-implementation/02-domain-to-code-mapping.md |
| "Microservice code model structure", "Directory layout, package conventions, layered code" | references/05-implementation/03-code-model-structure.md |
| "Service and data collaboration in layers", "DO/DTO/VO/PO transformation chain", "Strict layering service calls" | references/05-implementation/04-service-data-collaboration.md |
| "Microservice boundaries and evolution", "Logical/physical/code boundaries", "How to evolve architecture safely" | references/05-implementation/05-boundaries-and-evolution.md |
| "Middle platform (中台) design with DDD", "Platform vs Middle Platform, 5-step modeling", "Top-down vs bottom-up strategy" | references/06-enterprise/01-middle-platform-design.md |
| "Microservice design principles and strategies", "Strangler vs Repairer pattern", "4 design principles" | references/06-enterprise/02-microservice-design-principles.md |
| "Distributed architecture FAQ", "10 key questions: sharding, CDC, caching, BFF, multi-DC" | references/06-enterprise/03-distributed-architecture-faq.md |
End-to-End Examples
Examples differ from references: references teach concepts and principles; examples demonstrate application in concrete business contexts with real code. Load these when the user wants to see DDD applied to a realistic scenario.
| Business Scenario | What it demonstrates | When to load |
|---|---|---|
| Attendance System | Full workflow: event storming → aggregate design → service identification → code structure. Includes non-typical aggregate handling. | "Show me a complete DDD example", "Walk through DDD end-to-end", "考勤 DDD 案例" |
| E-Commerce Order | Core order domain: aggregate design with Order/OrderItem, value objects (Money, OrderStatus), domain events, complete layered code. | "E-commerce DDD example", "Order aggregate design", "电商 DDD 案例" |
| Insurance Policy | Complex aggregate design: entity vs VO decisions, invariants enforcement, domain service for cross-entity logic. | "Insurance DDD example", "Complex aggregate design", "保险 DDD 案例" |
Principle: "Don't apply DDD everywhere. Apply it where it matters."
>
After this, useddd-architecture-selectorto choose the right architecture pattern, then dive into domain modeling withddd-domain-designerand implementation with the specific architecture skill.
---
Gotchas — Common Pitfalls
- "DDD = COLA" 误解: DDD 不是等于 COLA 框架。COLA 只是阿里体系中一种落地方案。还有 Layered、Onion、Hexagonal、Clean 等多种架构可选。根据项目实际需求选择。
- 一上来就搞 CQRS + Event Sourcing: 绝大多数项目不需要 Event Sourcing。先评估是否真的需要完整审计追踪和时间旅行查询。从最简单的分层架构开始。
- 把 DDD 当银弹: DDD 适合复杂业务领域,对于简单 CRUD 项目反而是过度设计。判断标准:业务规则 > CRUD 操作,有领域专家参与,需要长期维护。
- 战术设计先于战略设计: 直接写 Entity/ValueObject 而不先做 Bounded Context 划分。没有战略边界的战术设计会导致上下文混乱和模型冲突。
- 领域专家参与不足: DDD 需要领域专家持续参与。开发人员凭自己对业务的理解建模,产出的模型很可能偏离真实业务。
When NOT to Use This Skill
| ❌ Skip | ✅ Use Instead |
|---|---|
| Already know DDD well | Jump to architecture-selector |
| Just need architecture comparison | architecture-selector (faster) |
| Want code immediately, not concepts | Pick an Architecture Skill directly |
| Building a simple prototype | Skip DDD, use simple Spring Boot MVC |
| Existing project with working architecture | architecture-evaluator (assess if DDD needed) |
Security & Stability
- All code examples and concept illustrations are for educational purposes. They contain no real credentials or sensitive data.
- This skill teaches DDD methodology and patterns. It does not execute code, access external systems, or modify any project files.
- DDD's bounded context isolation naturally promotes security boundaries between different parts of the system.
- No executable scripts bundled. This skill is a pure learning and reference resource.
🧭 DDD Skills Journey
📍 You are here: `ddd-architecture-awesome` — Step 1: DDD 入门与全景
flowchart LR
S1["⭐ Step 1<br/>awesome<br/>入门与全景"] --> S2["Step 2<br/>selector<br/>架构选型"]
S2 --> S3A["Step 3<br/>layered<br/>分层架构"]
S2 --> S3B["Step 3<br/>onion<br/>洋葱架构"]
S2 --> S3C["Step 3<br/>hexagonal<br/>六边形架构"]
S2 --> S3D["Step 3<br/>clean<br/>整洁架构"]
S2 --> S3E["Step 3<br/>cola<br/>COLA v5"]
S3A & S3B & S3C & S3D & S3E --> S4A["Step 4<br/>domain-designer<br/>领域建模"]
S3A & S3B & S3C & S3D & S3E --> S4B["Step 4<br/>cqrs-architecture<br/>CQRS"]
S3A & S3B & S3C & S3D & S3E --> S4C["Step 4<br/>api-designer<br/>API设计"]
S4A & S4B & S4C --> S5["Step 5<br/>code-reviewer<br/>代码审查"]
S5 --> S6A["Step 6<br/>event-storming<br/>事件风暴"]
S5 --> S6B["Step 6<br/>testing-strategist<br/>测试策略"]
S5 --> S6C["Step 6<br/>devops-integration<br/>DevOps"]
S5 --> S6D["Step 6<br/>evaluator<br/>架构评估"]
S6A & S6B & S6C & S6D --> S7["🏁 Step 7<br/>architecture-doc<br/>架构文档"]
style S1 fill:#10b981,stroke:#059669,color:white,stroke-width:3px← Previous: 你已经在 DDD 旅程的起点 🚀 → Next: selector — 选择适合你项目的架构模式 🔗 Related: domain-designer — 直接开始领域建模 | event-storming — 先做事件风暴工作坊 🏠 Home: 你在起点
💡 你是第一次接触 DDD?读完 Section 4 的 Pattern Boundaries 表和 Section 6 的核心概念速查,然后去 selector 选架构。
📋 See DESIGN.md for the complete 16-skill ecosystem map.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.tailwindcss.com"></script>
<title>TRACE 评测报告 — ddd-architecture-awesome</title>
<style>
@media print { body { background: white !important; } .no-print { display: none !important; } }
</style>
</head>
<body class="bg-gray-50">
<div class="pb-8 max-w-5xl mx-auto px-4 pt-6">
<!-- Header -->
<div class="mb-6 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
<div>
<h1 class="text-2xl font-bold text-gray-900">ddd-architecture-awesome</h1>
<p class="text-sm text-gray-500 mt-0.5">full-stack-skills/skills/ddd-skills/ddd-architecture-awesome</p>
</div>
<div class="flex items-center gap-2 text-xs text-gray-400">
<span>评测时间:2026-05-29</span>
<span class="px-2 py-0.5 rounded bg-green-50 text-green-700 font-medium">Official: ✅ Pass</span>
</div>
</div>
<!-- TRACE 维度说明卡 -->
<div class="mb-6 rounded-2xl overflow-hidden border" style="border-color:rgba(63,94,255,0.16)">
<div class="h-1" style="background:linear-gradient(90deg,#3f5eff 0%,#af52de 100%)"></div>
<div class="p-5" style="background:linear-gradient(135deg,rgba(63,94,255,0.04) 0%,rgba(175,82,222,0.04) 100%)">
<div class="flex items-start gap-3">
<div class="w-9 h-9 rounded-xl bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center flex-shrink-0 mt-0.5 shadow-sm">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-[18px] h-[18px] text-white"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
</div>
<div>
<h3 class="text-[15px] font-semibold text-gray-900 mb-1.5">TRACE 评测维度说明</h3>
<p class="text-[13px] leading-relaxed text-gray-600">
SkillHub TRACE 评测体系从 <span class="font-medium text-gray-800">可信任度(Trust)</span>、<span class="font-medium text-gray-800">可靠性(Reliability)</span>、<span class="font-medium text-gray-800">适用性(Adaptability)</span>、<span class="font-medium text-gray-800">规范性(Convention)</span>、<span class="font-medium text-gray-800">有效性(Effectiveness)</span> 五个维度全面评估 Skill 的质量。该体系基于 SkillHub 平台的长期运营实践及腾讯新闻团队的内容生产经验沉淀而成,旨在帮助用户快速识别高质量 Skill。
</p>
<p class="text-[12px] text-gray-500 mt-2 flex items-center gap-1">
<span class="inline-block w-1.5 h-1.5 rounded-full bg-amber-400"></span>
特别说明:本报告不是官方报告,而是 TRACE 评测体系的模拟检测,最终测评结果以 SkillHub 为准。
</p>
<p class="text-[12px] text-gray-400 mt-1">
等级阈值:≥4.7 优秀(Excellent) | ≥4.2 良好(Good) | ≥3.5 一般(Fair) | <3.5 待改进(Needs improvement)
</p>
</div>
</div>
</div>
</div>
<!-- Overall Score + Radar -->
<div class="mb-6 p-6 rounded-2xl bg-white border border-gray-100 shadow-sm">
<div class="flex flex-col lg:flex-row items-center gap-8">
<div class="w-full lg:w-[320px] h-[260px] flex-shrink-0">
<svg width="320" height="260" viewBox="0 0 320 260">
<line stroke="#e5e7eb" stroke-width="0.5" x1="160" y1="130" x2="160" y2="40"/>
<line stroke="#e5e7eb" stroke-width="0.5" x1="160" y1="130" x2="245.595" y2="102.188"/>
<line stroke="#e5e7eb" stroke-width="0.5" x1="160" y1="130" x2="212.901" y2="202.812"/>
<line stroke="#e5e7eb" stroke-width="0.5" x1="160" y1="130" x2="107.099" y2="202.812"/>
<line stroke="#e5e7eb" stroke-width="0.5" x1="160" y1="130" x2="74.405" y2="102.188"/>
<path stroke="#e5e7eb" stroke-width="0.5" fill="none" d="M 160,112 L 177.119,124.438 L 170.580,144.562 L 149.420,144.562 L 142.881,124.438 Z"/>
<path stroke="#e5e7eb" stroke-width="0.5" fill="none" d="M 160,94 L 194.238,118.875 L 181.160,159.125 L 138.840,159.125 L 125.762,118.875 Z"/>
<path stroke="#e5e7eb" stroke-width="0.5" fill="none" d="M 160,76 L 211.357,113.313 L 191.740,173.687 L 128.260,173.687 L 108.643,113.313 Z"/>
<path stroke="#e5e7eb" stroke-width="0.5" fill="none" d="M 160,58 L 228.476,107.751 L 202.321,188.249 L 117.679,188.249 L 91.524,107.751 Z"/>
<path stroke="#e5e7eb" stroke-width="0.5" fill="none" d="M 160,40 L 245.595,102.188 L 212.901,202.812 L 107.099,202.812 L 74.405,102.188 Z"/>
<polygon points="160,42.8 241.599,104.340 208.834,197.612 111.166,197.612 78.401,104.340" fill="rgba(99,102,241,0.18)" stroke="#6366F1" stroke-width="2" stroke-linejoin="round"/>
<circle cx="160" cy="42.8" r="4" fill="#ef4444"/>
<circle cx="241.599" cy="104.340" r="4" fill="#f59e0b"/>
<circle cx="208.834" cy="197.612" r="4" fill="#3b82f6"/>
<circle cx="111.166" cy="197.612" r="4" fill="#8b5cf6"/>
<circle cx="78.401" cy="104.340" r="4" fill="#10b981"/>
<text font-size="12" text-anchor="middle" fill="#4B5563" x="160" y="28">T 可信任度</text>
<text font-size="12" text-anchor="start" fill="#4B5563" x="257" y="98">R 可靠性</text>
<text font-size="12" text-anchor="start" fill="#4B5563" x="220" y="213">A 适用性</text>
<text font-size="12" text-anchor="end" fill="#4B5563" x="100" y="213">C 规范性</text>
<text font-size="12" text-anchor="end" fill="#4B5563" x="63" y="98">E 有效性</text>
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-baseline gap-3 mb-4">
<span class="text-[48px] font-bold text-gray-900 leading-none">4.71</span>
<span class="text-[16px] text-gray-400 font-medium">/ 5</span>
</div>
<div class="mb-3">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-[13px] font-semibold bg-green-100 text-green-800">🟢 优秀(Excellent)</span>
</div>
<p class="text-[14px] leading-relaxed text-gray-600">结构清晰、内容完整的 DDD 知识体系入口技能。27 个分类参考文献形成六层知识金字塔,11 条领域专属 Gotchas 精准预防常见误区,3 个端到端案例覆盖不同复杂度场景。</p>
<p class="text-[13px] text-gray-500 mt-2">整体来看,<strong>可信任度(T)和规范性(C)表现最佳</strong>,适用性(A)和可靠性(R)相对薄弱,主要因为边界信息分散在三处未形成独立章节、缺少交互式引导模板。</p>
<div class="mt-4 grid grid-cols-5 gap-2">
<div class="text-center rounded-lg p-2" style="background:rgba(239,68,68,0.06)"><div class="text-[18px] font-bold" style="color:#ef4444">4.88</div><div class="text-[10px] text-gray-400">T·Trust</div></div>
<div class="text-center rounded-lg p-2" style="background:rgba(245,158,11,0.06)"><div class="text-[18px] font-bold" style="color:#f59e0b">4.63</div><div class="text-[10px] text-gray-400">R·Reliability</div></div>
<div class="text-center rounded-lg p-2" style="background:rgba(59,130,246,0.06)"><div class="text-[18px] font-bold" style="color:#3b82f6">4.63</div><div class="text-[10px] text-gray-400">A·Adaptability</div></div>
<div class="text-center rounded-lg p-2" style="background:rgba(139,92,246,0.06)"><div class="text-[18px] font-bold" style="color:#8b5cf6">4.75</div><div class="text-[10px] text-gray-400">C·Convention</div></div>
<div class="text-center rounded-lg p-2" style="background:rgba(16,185,129,0.06)"><div class="text-[18px] font-bold" style="color:#10b981">4.65</div><div class="text-[10px] text-gray-400">E·Effectiveness</div></div>
</div>
</div>
</div>
</div>
<!-- Dimension Detail Cards -->
<div class="space-y-4">
<h3 class="text-[16px] font-semibold text-gray-900">📊 五维度详析(20 子项评分)</h3>
<!-- T -->
<div class="p-5 rounded-xl border border-gray-100 bg-white">
<div class="flex items-center gap-3 mb-3">
<div class="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0" style="background-color:rgba(16,185,129,0.082)"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="w-4 h-4" style="color:rgb(16,185,129)"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/></svg></div>
<div class="flex-1"><span class="text-[14px] font-semibold text-gray-900">T · Trust(可信任度)</span><span class="text-[12px] text-gray-500 ml-2">安全红线维度</span><span class="float-right text-[18px] font-bold" style="color:rgb(16,185,129)">4.88</span></div>
</div>
<div class="flex items-center gap-3 mb-3"><div class="flex-1 h-1.5 rounded-full bg-gray-100 overflow-hidden"><div class="h-full rounded-full" style="width:97.6%;background:linear-gradient(90deg,#10b981,#34d399)"></div></div></div>
<p class="text-[13px] text-gray-600 mb-2">零脚本纯文档型、无任何安全风险,全篇中英双语完整覆盖,安全声明明确(4条)。边界信息分散在3处,未形成独立三分类章节。</p>
<div class="overflow-x-auto">
<table class="w-full text-[13px]">
<tr class="bg-gray-50 text-gray-500 text-[12px]"><th class="text-left p-2">子项</th><th class="p-2 w-14">得分</th><th class="text-left p-2">证据</th><th class="text-left p-2">建议</th></tr>
<tr><td class="p-2 font-medium">T1 安全性</td><td class="p-2 font-bold text-green-600">5.0</td><td class="p-2 text-gray-600">无 secrets;无 scripts/;Security & Stability 4条声明</td><td class="p-2 text-gray-400">—</td></tr>
<tr><td class="p-2 font-medium">T2 国内适配</td><td class="p-2 font-bold text-green-600">5.0</td><td class="p-2 text-gray-600">全篇中英双语;架构中文标注;Spring Boot 生态</td><td class="p-2 text-gray-400">—</td></tr>
<tr><td class="p-2 font-medium">T3 边界控制</td><td class="p-2 font-bold text-blue-600">4.5</td><td class="p-2 text-gray-600">三重边界但分散 3 处,未独立三分类章节</td><td class="p-2 text-amber-600">整合为独立边界章节(✅/⚠️/❌)</td></tr>
<tr><td class="p-2 font-medium">T4 安全声明</td><td class="p-2 font-bold text-green-600">5.0</td><td class="p-2 text-gray-600">教育用途+不执行代码+BC隔离+无脚本</td><td class="p-2 text-gray-400">—</td></tr>
</table>
</div>
</div>
<!-- R -->
<div class="p-5 rounded-xl border border-gray-100 bg-white">
<div class="flex items-center gap-3 mb-3">
<div class="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0" style="background-color:rgba(59,130,246,0.082)"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="w-4 h-4" style="color:rgb(59,130,246)"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M8 16H3v5"/></svg></div>
<div class="flex-1"><span class="text-[14px] font-semibold text-gray-900">R · Reliability(可靠性)</span><span class="text-[12px] text-gray-500 ml-2">稳定性与交付</span><span class="float-right text-[18px] font-bold" style="color:rgb(59,130,246)">4.63</span></div>
</div>
<div class="flex items-center gap-3 mb-3"><div class="flex-1 h-1.5 rounded-full bg-gray-100 overflow-hidden"><div class="h-full rounded-full" style="width:92.6%;background:linear-gradient(90deg,#3b82f6,#60a5fa)"></div></div></div>
<p class="text-[13px] text-gray-600 mb-2">Gotchas 质量高(11条具体领域陷阱+纠正),降级兜底优秀(每场景有替代Skill引导)。缺少交互式引导模板,steps=4未达满分阈值。</p>
<div class="overflow-x-auto">
<table class="w-full text-[13px]">
<tr class="bg-gray-50 text-gray-500 text-[12px]"><th class="text-left p-2">子项</th><th class="p-2 w-14">得分</th><th class="text-left p-2">证据</th><th class="text-left p-2">建议</th></tr>
<tr><td class="p-2 font-medium">R1 异常处理</td><td class="p-2 font-bold text-blue-600">4.5</td><td class="p-2 text-gray-600">11条Gotchas+代码异常;缺交互式引导模板</td><td class="p-2 text-amber-600">加"先给假设版本+列缺失项"模板</td></tr>
<tr><td class="p-2 font-medium">R2 可运行性</td><td class="p-2 font-bold text-blue-600">4.5</td><td class="p-2 text-gray-600">4步Workflow+3examples;steps<5</td><td class="p-2 text-amber-600">加第5步"输入不足时的假设处理"</td></tr>
<tr><td class="p-2 font-medium">R3 交付完整</td><td class="p-2 font-bold text-blue-600">4.5</td><td class="p-2 text-gray-600">Gotchas+反例对比+输出模板;缺校验清单</td><td class="p-2 text-amber-600">加 plan-validate-execute 循环</td></tr>
<tr><td class="p-2 font-medium">R4 降级兜底</td><td class="p-2 font-bold text-green-600">5.0</td><td class="p-2 text-gray-600">5场景均有替代Skill引导;手动校准+1.5</td><td class="p-2 text-gray-400">—</td></tr>
</table>
</div>
</div>
<!-- A -->
<div class="p-5 rounded-xl border border-gray-100 bg-white">
<div class="flex items-center gap-3 mb-3">
<div class="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0" style="background-color:rgba(245,158,11,0.082)"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="w-4 h-4" style="color:rgb(245,158,11)"><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/></svg></div>
<div class="flex-1"><span class="text-[14px] font-semibold text-gray-900">A · Adaptability(适用性)</span><span class="text-[12px] text-gray-500 ml-2">场景识别与触发</span><span class="float-right text-[18px] font-bold" style="color:rgb(245,158,11)">4.63</span></div>
</div>
<div class="flex items-center gap-3 mb-3"><div class="flex-1 h-1.5 rounded-full bg-gray-100 overflow-hidden"><div class="h-full rounded-full" style="width:92.6%;background:linear-gradient(90deg,#f59e0b,#fbbf24)"></div></div></div>
<p class="text-[13px] text-gray-600 mb-2">双重边界+场景化路由+近失覆盖完整;Description 触发质量完美(653chars)。缺少显式受众分级,场景覆盖面可进一步扩展。</p>
<div class="overflow-x-auto">
<table class="w-full text-[13px]">
<tr class="bg-gray-50 text-gray-500 text-[12px]"><th class="text-left p-2">子项</th><th class="p-2 w-14">得分</th><th class="text-left p-2">证据</th><th class="text-left p-2">建议</th></tr>
<tr><td class="p-2 font-medium">A1 边界清晰</td><td class="p-2 font-bold text-blue-600">4.8</td><td class="p-2 text-gray-600">双重边界+场景化路由+近失覆盖;手动校准+0.8</td><td class="p-2 text-amber-600">整合独立三分类边界章节</td></tr>
<tr><td class="p-2 font-medium">A2 触发质量</td><td class="p-2 font-bold text-green-600">5.0</td><td class="p-2 text-gray-600">653chars祈使句+中英关键词</td><td class="p-2 text-gray-400">—</td></tr>
<tr><td class="p-2 font-medium">A3 受众广度</td><td class="p-2 font-bold text-blue-600">4.5</td><td class="p-2 text-gray-600">中英双语;未显式声明目标受众分级</td><td class="p-2 text-amber-600">加受众分级(新手/架构师/开发者)</td></tr>
<tr><td class="p-2 font-medium">A4 场景覆盖</td><td class="p-2 font-bold text-amber-600">4.2</td><td class="p-2 text-gray-600">3examples+27refs;可扩展IoT/金融场景</td><td class="p-2 text-amber-600">增加更多复杂度场景 example</td></tr>
</table>
</div>
</div>
<!-- C -->
<div class="p-5 rounded-xl border border-gray-100 bg-white">
<div class="flex items-center gap-3 mb-3">
<div class="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0" style="background-color:rgba(139,92,246,0.082)"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="w-4 h-4" style="color:rgb(139,92,246)"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg></div>
<div class="flex-1"><span class="text-[14px] font-semibold text-gray-900">C · Convention(规范性)</span><span class="text-[12px] text-gray-500 ml-2">结构与可维护性</span><span class="float-right text-[18px] font-bold" style="color:rgb(139,92,246)">4.75</span></div>
</div>
<div class="flex items-center gap-3 mb-3"><div class="flex-1 h-1.5 rounded-full bg-gray-100 overflow-hidden"><div class="h-full rounded-full" style="width:95%;background:linear-gradient(90deg,#8b5cf6,#c4b5fd)"></div></div></div>
<p class="text-[13px] text-gray-600 mb-2">渐进披露优秀(27文件6级分类+逐一触发条件);Gotchas精准实用。SKILL.md 505行略超推荐500上限,可迁移Sources章节到references。</p>
<div class="overflow-x-auto">
<table class="w-full text-[13px]">
<tr class="bg-gray-50 text-gray-500 text-[12px]"><th class="text-left p-2">子项</th><th class="p-2 w-14">得分</th><th class="text-left p-2">证据</th><th class="text-left p-2">建议</th></tr>
<tr><td class="p-2 font-medium">C1 用途说明</td><td class="p-2 font-bold text-blue-600">4.7</td><td class="p-2 text-gray-600">三重用途说明+触发条件;上调+0.2</td><td class="p-2 text-gray-400">—</td></tr>
<tr><td class="p-2 font-medium">C2 渐进披露</td><td class="p-2 font-bold text-blue-600">4.5</td><td class="p-2 text-gray-600">6子目录27文件按序号;SKILL.md 505略超上限</td><td class="p-2 text-amber-600">Sources章节移到references</td></tr>
<tr><td class="p-2 font-medium">C3 元信息</td><td class="p-2 font-bold text-green-600">5.0</td><td class="p-2 text-gray-600">name有效+匹配+Apache-2.0+编号有序</td><td class="p-2 text-gray-400">—</td></tr>
<tr><td class="p-2 font-medium">C4 Gotchas</td><td class="p-2 font-bold text-green-600">4.8</td><td class="p-2 text-gray-600">11条精准领域陷阱+纠正建议</td><td class="p-2 text-gray-400">—</td></tr>
</table>
</div>
</div>
<!-- E -->
<div class="p-5 rounded-xl border border-gray-100 bg-white">
<div class="flex items-center gap-3 mb-3">
<div class="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0" style="background-color:rgba(239,68,68,0.082)"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="w-4 h-4" style="color:rgb(239,68,68)"><path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/></svg></div>
<div class="flex-1"><span class="text-[14px] font-semibold text-gray-900">E · Effectiveness(有效性)</span><span class="text-[12px] text-gray-500 ml-2">任务增益与代价</span><span class="float-right text-[18px] font-bold" style="color:rgb(239,68,68)">4.65</span></div>
</div>
<div class="flex items-center gap-3 mb-3"><div class="flex-1 h-1.5 rounded-full bg-gray-100 overflow-hidden"><div class="h-full rounded-full" style="width:93%;background:linear-gradient(90deg,#ef4444,#f87171)"></div></div></div>
<p class="text-[13px] text-gray-600 mb-2">12章完整知识链条;27refs可追溯;增益vs no-skill显著(反模式↑↑↑、知识追溯↑↑↑、架构选型↑↑↑)。</p>
<div class="overflow-x-auto">
<table class="w-full text-[13px]">
<tr class="bg-gray-50 text-gray-500 text-[12px]"><th class="text-left p-2">子项</th><th class="p-2 w-14">得分</th><th class="text-left p-2">证据</th><th class="text-left p-2">建议</th></tr>
<tr><td class="p-2 font-medium">E1 任务完成</td><td class="p-2 font-bold text-blue-600">4.8</td><td class="p-2 text-gray-600">12章完整覆盖:定义→决策树→架构→概念→反模式→路径</td><td class="p-2 text-gray-400">—</td></tr>
<tr><td class="p-2 font-medium">E2 正确权威</td><td class="p-2 font-bold text-amber-600">4.3</td><td class="p-2 text-gray-600">引用Evans/Vernon/Fowler等;ASCII图轻微对齐问题</td><td class="p-2 text-amber-600">修复窄终端ASCII图对齐</td></tr>
<tr><td class="p-2 font-medium">E3 交付质量</td><td class="p-2 font-bold text-blue-600">4.7</td><td class="p-2 text-gray-600">27refs+6层体系+3examples结构化输出</td><td class="p-2 text-gray-400">—</td></tr>
<tr><td class="p-2 font-medium">E4 增益归因</td><td class="p-2 font-bold text-blue-600">4.8</td><td class="p-2 text-gray-600">vs no-skill:反模式↑↑↑、知识追溯↑↑↑、架构选型↑↑↑</td><td class="p-2 text-gray-400">—</td></tr>
</table>
</div>
</div>
</div>
<!-- Baseline -->
<div class="mt-4 p-5 rounded-xl border border-gray-100 bg-white">
<h3 class="text-[15px] font-semibold text-gray-900 mb-3">📊 no-skill 基线对比</h3>
<div class="overflow-x-auto">
<table class="w-full text-[13px]">
<tr class="bg-gray-50 text-gray-500 text-[12px]"><th class="text-left p-2">对比维度</th><th class="p-2">no-skill(裸模型)</th><th class="p-2">启用此 skill</th><th class="p-2 w-16">增益</th></tr>
<tr><td class="p-2">DDD 概念准确性</td><td class="p-2 text-gray-500">中等(依赖训练数据)</td><td class="p-2">高(Evans/Vernon/Fowler 等权威源)</td><td class="p-2 font-bold text-green-600">↑↑</td></tr>
<tr><td class="p-2">适用场景判断</td><td class="p-2 text-gray-500">通用化建议</td><td class="p-2">结构化决策树+四维度评估表</td><td class="p-2 font-bold text-green-600">↑↑</td></tr>
<tr><td class="p-2">架构选型指导</td><td class="p-2 text-gray-500">可能偏颇</td><td class="p-2">5 种架构平等对比+中文生态</td><td class="p-2 font-bold text-green-600">↑↑↑</td></tr>
<tr><td class="p-2">反模式检测</td><td class="p-2 text-gray-500">泛泛而谈</td><td class="p-2">10 条精确反模式+修复+代码对比</td><td class="p-2 font-bold text-green-600">↑↑↑</td></tr>
<tr><td class="p-2">知识可追溯性</td><td class="p-2 text-gray-500">无</td><td class="p-2">27 文件+完整引用链+多语言参考实现</td><td class="p-2 font-bold text-green-600">↑↑↑</td></tr>
<tr><td class="p-2">Token 成本</td><td class="p-2 text-gray-500">0</td><td class="p-2">~3,000 tokens</td><td class="p-2 font-bold text-amber-600">中等</td></tr>
</table>
</div>
</div>
<!-- Official -->
<div class="mt-4 p-5 rounded-xl border border-gray-100 bg-white">
<h3 class="text-[15px] font-semibold text-gray-900 mb-3">📋 官方规范合规(agentskills.io)</h3>
<div class="rounded-lg p-3 mb-3" style="background:rgba(16,185,129,0.06);border-left:3px solid rgb(16,185,129)">
<span class="text-[14px] font-semibold" style="color:rgb(16,185,129)">✅ 全部通过(Pass)— 0 Issues</span>
<span class="text-[12px] text-gray-500 ml-2">基于 agentskills.io 官方规范</span>
</div>
<div class="overflow-x-auto">
<table class="w-full text-[13px]">
<tr class="bg-gray-50 text-gray-500 text-[12px]"><th class="text-left p-2 w-8">#</th><th class="text-left p-2">检查项</th><th class="p-2 w-16">结果</th><th class="text-left p-2">证据</th></tr>
<tr><td class="p-2">1</td><td class="p-2">SKILL.md 存在</td><td class="p-2 text-green-600 font-bold">✅</td><td class="p-2 text-gray-500">SKILL.md 位于技能根目录</td></tr>
<tr><td class="p-2">2</td><td class="p-2">Name 与目录名一致</td><td class="p-2 text-green-600 font-bold">✅</td><td class="p-2 text-gray-500">name=ddd-architecture-awesome</td></tr>
<tr><td class="p-2">3</td><td class="p-2">Name 格式有效(kebab-case)</td><td class="p-2 text-green-600 font-bold">✅</td><td class="p-2 text-gray-500">小写字母+连字符,<64字符</td></tr>
<tr><td class="p-2">4</td><td class="p-2">Description 有效</td><td class="p-2 text-green-600 font-bold">✅</td><td class="p-2 text-gray-500">653 chars,做什么+何时使用</td></tr>
<tr><td class="p-2">5</td><td class="p-2">License 字段</td><td class="p-2 text-green-600 font-bold">✅</td><td class="p-2 text-gray-500">Apache-2.0</td></tr>
<tr><td class="p-2">6</td><td class="p-2">目录结构规范</td><td class="p-2 text-green-600 font-bold">✅</td><td class="p-2 text-gray-500">references(6subdirs/27files)+examples(3files)</td></tr>
<tr><td class="p-2">7</td><td class="p-2">渐进式披露质量</td><td class="p-2 text-green-600 font-bold">✅</td><td class="p-2 text-gray-500">SKILL.md~505lines;Reference Library每条有触发条件</td></tr>
<tr><td class="p-2">8</td><td class="p-2">Description 触发质量</td><td class="p-2 text-green-600 font-bold">✅</td><td class="p-2 text-gray-500">祈使句+中英文触发词</td></tr>
<tr><td class="p-2">9</td><td class="p-2">脚本安全性</td><td class="p-2 text-gray-400 font-bold">N/A</td><td class="p-2 text-gray-500">无 scripts/ 目录</td></tr>
<tr><td class="p-2">10</td><td class="p-2">密钥/敏感信息扫描</td><td class="p-2 text-green-600 font-bold">✅</td><td class="p-2 text-gray-500">未检出任何 secret</td></tr>
</table>
</div>
</div>
<!-- Suggestions -->
<div class="mt-4 p-5 rounded-xl border border-gray-100 bg-white">
<h3 class="text-[15px] font-semibold text-gray-900 mb-3">💡 优化建议</h3>
<div class="space-y-2">
<div class="flex items-start gap-3 p-3 rounded-lg" style="background:rgba(239,68,68,0.04)">
<span class="px-2 py-0.5 rounded text-[11px] font-bold bg-red-100 text-red-700">P1</span>
<div><span class="text-[13px] font-medium text-gray-800">T3/A1 · 边界整合</span><p class="text-[12px] text-gray-500 mt-0.5">将分散的3处边界信息整合为独立三分类章节(✅擅长/⚠️需条件/❌超范围),每类≥3例,可达到 T3=5.0 + A1=5.0</p></div>
</div>
<div class="flex items-start gap-3 p-3 rounded-lg" style="background:rgba(245,158,11,0.04)">
<span class="px-2 py-0.5 rounded text-[11px] font-bold bg-amber-100 text-amber-700">P2</span>
<div><span class="text-[13px] font-medium text-gray-800">R1 · 交互式引导模板</span><p class="text-[12px] text-gray-500 mt-0.5">信息不足时先给假设版本的DDD适用性评估+列缺少的信息,替代笼统的"请提供更多信息",可达到 R1=5.0</p></div>
</div>
<div class="flex items-start gap-3 p-3 rounded-lg" style="background:rgba(59,130,246,0.04)">
<span class="px-2 py-0.5 rounded text-[11px] font-bold bg-blue-100 text-blue-700">P3</span>
<div><span class="text-[13px] font-medium text-gray-800">A3 · 受众分级</span><p class="text-[12px] text-gray-500 mt-0.5">增加受众分级段落:DDD新手路径 vs 架构师路径 vs 开发者路径</p></div>
</div>
</div>
</div>
<!-- Skill Profile -->
<div class="mt-4 p-5 rounded-xl border border-gray-100 bg-white">
<h3 class="text-[15px] font-semibold text-gray-900 mb-3">📦 Skill 基础画像</h3>
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3 text-[13px]">
<div class="p-2 rounded-lg bg-gray-50"><span class="text-gray-400">路径</span><br><span class="font-medium">skills/ddd-skills/ddd-architecture-awesome</span></div>
<div class="p-2 rounded-lg bg-gray-50"><span class="text-gray-400">SKILL.md</span><br><span class="font-medium">505 lines · 29,480 chars</span></div>
<div class="p-2 rounded-lg bg-gray-50"><span class="text-gray-400">References</span><br><span class="font-medium">27 files · 6 subdirs</span></div>
<div class="p-2 rounded-lg bg-gray-50"><span class="text-gray-400">Examples</span><br><span class="font-medium">3 end-to-end cases</span></div>
<div class="p-2 rounded-lg bg-gray-50"><span class="text-gray-400">Scripts</span><br><span class="font-medium">0</span></div>
<div class="p-2 rounded-lg bg-gray-50"><span class="text-gray-400">License</span><br><span class="font-medium">Apache-2.0</span></div>
<div class="p-2 rounded-lg bg-gray-50"><span class="text-gray-400">Gotchas</span><br><span class="font-medium">11 domain-specific</span></div>
<div class="p-2 rounded-lg bg-gray-50"><span class="text-gray-400">Skill Type</span><br><span class="font-medium">prompt</span></div>
</div>
</div>
<!-- Footer -->
<div class="mt-6 text-center text-[12px] text-gray-400 pb-4">
<p>评估依据:<strong>SkillHub TRACE 评测体系</strong></p>
<p>合规检查:<strong>agentskills.io</strong> 官方规范 · Generated 2026-05-29</p>
</div>
</div>
</body>
</html>考勤请假系统 — DDD 端到端全流程案例
来源:极客时间 DDD实战课 第18讲 | 展示从战略设计到战术设计的完整过程
项目背景
在线请假考勤系统:请假人提交审批 → 逐级核批 → 考勤统计。同时支持内外网,内外部人员无差异管理。
一、战略设计(事件风暴)
1.1 场景分析
| 场景 | 角色 | 关键操作 |
|---|---|---|
| 请假 | 请假人 | 创建请假单 → 提交审批 |
| 审批 | 审批人 | 获取待审批列表 → 逐级审批 |
| 考勤 | 系统 | 核销请假数据 → 输出考勤统计 |
1.2 领域对象提取
| 实体/值对象 | 类型 | 来源 |
|---|---|---|
| 请假单 | 聚合根 | 请假场景 |
| 审批意见 | 实体 | 审批场景 |
| 审批规则 | 值对象 | 审批场景 |
| 人员 | 聚合根 | 人员组织场景 |
| 组织关系 | 实体 | 人员组织场景 |
| 刷卡明细 | 实体 | 考勤场景 |
| 考勤明细 | 实体 | 考勤场景 |
| 考勤统计 | 实体 | 考勤场景 |
1.3 聚合设计
请假聚合:
├── 请假单 (聚合根)
│ ├── 属性:请假类型、起止时间、天数
│ ├── 方法:create(), submit(), approve(), reject()
│ └── 值对象:请假人、审批人、请假类型、审批状态、审批规则
├── 审批意见 (实体) — 多级审批产生多条
│ ├── 属性:审批人、审批状态、审批意见
│ └── 方法:agree(), reject()
└── 审批规则 (值对象)
└── 根据请假类型和天数确定审批层级
人员组织关系聚合:
├── 人员 (聚合根)
│ ├── 属性:姓名、工号、岗位、部门
│ └── 方法:getSupervisor(orgType)
└── 组织关系 (实体)
└── 属性:组织关系类型、上级领导
考勤聚合:(非典型聚合,无聚合根)
├── 刷卡明细 (实体)
├── 考勤明细 (实体) — 核销请假后生成
└── 考勤统计 (实体) — 汇总计算1.4 限界上下文 & 微服务拆分
| 限界上下文 | 包含聚合 | 微服务 | 拆分理由 |
|---|---|---|---|
| 请假上下文 | 请假 + 人员组织关系 | ✓ 请假微服务 | 共同完成请假功能 |
| 考勤上下文 | 考勤 | ✓ 考勤微服务 | 职责单一 |
二、战术设计(请假微服务为例)
2.1 服务识别
提交审批流程:
├── 应用服务:LeaveApplicationService.submitForApproval(leaveId)
├── 领域服务:
│ ├── ApprovalRuleService.getApprovalRule(leaveType, duration) → 审批规则
│ │ └── ApprovalRule.queryRule(leaveType, duration)
│ └── PersonService.getApprover(roleType) → 审批人
│ └── Person.getSupervisor(orgType)
└── 实体方法:
└── Leave.assignApprover(approver, rule) → 分配审批人并保存规则2.2 领域事件
| 事件 | 触发时机 | 后续操作 |
|---|---|---|
| LeaveSubmitted | 请假单提交 | 通知审批人 |
| LeaveApproved | 审批通过 | 发送请假数据到考勤微服务 |
| LeaveRejected | 审批驳回 | 通知请假人 |
2.3 代码结构
leave-microservice/
├── interfaces/
│ ├── controller/LeaveController.java
│ ├── dto/LeaveDTO.java, ApprovalDTO.java
│ └── assembler/LeaveAssembler.java
├── application/
│ ├── service/LeaveAppService.java # 编排
│ └── event/publish/LeaveEventPublisher.java
├── domain/
│ ├── leave/ # 请假聚合
│ │ ├── entity/Leave.java (聚合根), Approval.java (实体)
│ │ ├── vo/LeaveType.java, ApprovalStatus.java, Approver.java
│ │ ├── service/ApprovalRuleService.java
│ │ ├── event/LeaveSubmittedEvent.java, LeaveApprovedEvent.java
│ │ └── repository/LeaveRepository.java (接口)
│ └── person/ # 人员组织关系聚合
│ ├── entity/Person.java (聚合根), OrgRelation.java (实体)
│ ├── vo/OrgType.java
│ ├── service/PersonService.java
│ └── repository/PersonRepository.java (接口)
└── infrastructure/
├── persistence/
│ ├── LeaveRepositoryImpl.java
│ └── PersonRepositoryImpl.java
└── config/2.4 关键代码
// 聚合根 — 请假单
public class Leave extends AggregateRoot<LeaveId> {
private LeaveId id;
private Applicant applicant;
private LeaveType type;
private LocalDate startDate, endDate;
private LeaveStatus status;
private List<Approval> approvals = new ArrayList<>();
private ApprovalRule rule;
public void submit() {
if (status != LeaveStatus.DRAFT) throw new LeaveException("Only DRAFT can submit");
this.status = LeaveStatus.SUBMITTED;
addDomainEvent(new LeaveSubmittedEvent(this.id, this.applicant, this.type));
}
public void assignApprover(Approver approver, ApprovalRule rule) {
this.rule = rule;
this.approvals.add(new Approval(approver, ApprovalStatus.PENDING));
}
public void approve(Approver approver, String comment) {
Approval current = findPendingApproval(approver);
current.approve(comment);
Approval next = rule.getNextLevel(this.approvals.size());
if (next == null) {
this.status = LeaveStatus.APPROVED;
addDomainEvent(new LeaveApprovedEvent(this.id));
} else {
this.approvals.add(next);
}
}
}三、从本案例学到的关键点
1. 非典型聚合处理:考勤聚合无聚合根,仍用聚合封装,照常设计实体和仓储 2. 聚合间通过应用层编排:请假提交审批需要请假聚合 + 人员聚合协作,通过应用服务编排 3. 领域事件驱动跨微服务:审批通过后通过事件将请假数据发到考勤微服务 4. 聚合是微服务演进的最小单位:人员组织关系聚合可随时从请假微服务中独立出来
电商订单系统 — DDD 端到端示例
基于 DDD 的电商订单系统完整案例,覆盖从战略设计到代码落地的全过程。
业务背景
一个典型 B2C 电商平台的订单模块:
- 用户下单 → 库存扣减 → 支付 → 发货 → 签收
- 支持订单改价、取消、退款等逆向流程
- 与会员系统、商品系统、物流系统等外部上下文协作
战略设计(Strategic Design)
领域划分
| 域 | 类型 | 说明 |
|---|---|---|
| 订单域 | Core Domain | 核心利润来源,需要深度建模 |
| 支付域 | Supporting | 支撑订单完成,可对接第三方 |
| 商品域 | Supporting | 支撑下单展示,可独立演进 |
| 会员域 | Generic | 通用能力,可购买或开源 |
限界上下文
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Order BC │───→│ Payment BC │───→│ Gateway BC │
│ (订单上下文) │ │ (支付上下文) │ │ (支付网关) │
└──────┬───────┘ └──────────────┘ └──────────────┘
│
│ ACL (防腐层)
▼
┌──────────────┐ ┌──────────────┐
│ Product BC │ │ Member BC │
│ (商品上下文) │ │ (会员上下文) │
└──────────────┘ └──────────────┘上下文映射
| 关系 | 上游 | 下游 | 模式 |
|---|---|---|---|
| Order → Payment | Payment BC | Order BC | Customer-Supplier |
| Order → Product | Product BC | Order BC | ACL (Anti-Corruption Layer) |
| Order → Member | Member BC | Order BC | ACL |
| Payment → Gateway | Gateway BC | Payment BC | OHS (Open Host Service) |
战术设计(Tactical Design)
聚合设计
Order 聚合(聚合根:Order)
Order (Aggregate Root)
├── OrderId (VO)
├── BuyerId (VO — Member BC 的 ID 引用)
├── OrderStatus (VO: DRAFT → PAID → SHIPPED → DELIVERED → CANCELLED)
├── Money totalAmount (VO)
├── Address shippingAddress (VO)
├── List<OrderItem> items (Entity)
│ ├── ProductId (VO)
│ ├── Money unitPrice (VO)
│ └── int quantity
├── ── 行为 ──
├── void place() // 下单
├── void pay() // 支付完成
├── void ship() // 发货
├── void deliver() // 签收
├── void cancel(String reason) // 取消
└── ── 事件 ──
├── OrderPlacedEvent
├── OrderPaidEvent
├── OrderShippedEvent
└── OrderCancelledEvent聚合设计原则体现
| 原则 | 体现 |
|---|---|
| 小聚合 | Order 聚合仅包含 2 个实体(Order + OrderItem) |
| ID 引用 | Product、Member 通过 ID 引用,不持有对象 |
| 一致性边界 | order.pay() 只改变 Order 状态,库存扣减通过事件异步 |
| 不变量 | canceled 订单不可 pay,delivered 订单不可 cancel |
值对象设计
// 不可变值对象
public record Money(BigDecimal amount, Currency currency) {
public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new IllegalArgumentException("Currency mismatch");
}
return new Money(this.amount.add(other.amount), this.currency);
}
}
public record OrderStatus(String code) {
public static final OrderStatus DRAFT = new OrderStatus("DRAFT");
public static final OrderStatus PAID = new OrderStatus("PAID");
public static final OrderStatus SHIPPED = new OrderStatus("SHIPPED");
public static final OrderStatus DELIVERED = new OrderStatus("DELIVERED");
public static final OrderStatus CANCELLED = new OrderStatus("CANCELLED");
public boolean canPay() { return this == DRAFT; }
public boolean canShip() { return this == PAID; }
public boolean canCancel() { return this == DRAFT || this == PAID; }
}领域事件与流程
Order.placed → InventoryDeductionRequested → Inventory.deduct
Order.paid → PaymentConfirmed → Order.status=PAID
Order.paid → ShipmentRequested → Shipment.create
Shipment.picked → OrderShipped → Order.status=SHIPPED
Shipment.delivered → OrderDelivered → Order.status=DELIVERED分层架构落地
ecommerce-order/
├── order-adapter/ # 适配器层
│ ├── web/ # REST Controller
│ │ ├── OrderCommandController.java
│ │ └── OrderQueryController.java
│ └── event/ # 事件消费者
│ └── PaymentEventConsumer.java
├── order-app/ # 应用层
│ ├── command/
│ │ ├── PlaceOrderCommand.java
│ │ ├── PayOrderCommand.java
│ │ └── CancelOrderCommand.java
│ ├── query/
│ │ └── OrderQueryService.java
│ └── event/
│ └── OrderEventPublisher.java
├── order-domain/ # 领域层
│ ├── model/
│ │ ├── Order.java # 聚合根
│ │ ├── OrderItem.java # 实体
│ │ └── vo/ # 值对象
│ ├── service/
│ │ └── OrderDomainService.java
│ └── repository/
│ └── OrderRepository.java # 接口
└── order-infrastructure/ # 基础设施层
├── repository/
│ └── OrderRepositoryImpl.java
└── mapper/
└── OrderMapper.java关键代码片段
聚合根示例
public class Order extends AggregateRoot<OrderId> {
private OrderId id;
private BuyerId buyerId;
private OrderStatus status;
private Money totalAmount;
private Address shippingAddress;
private List<OrderItem> items = new ArrayList<>();
private LocalDateTime createdAt;
public Order(OrderId id, BuyerId buyerId, Address shippingAddress) {
this.id = id;
this.buyerId = buyerId;
this.status = OrderStatus.DRAFT;
this.shippingAddress = shippingAddress;
this.createdAt = LocalDateTime.now();
}
public void addItem(ProductId productId, Money unitPrice, int quantity) {
if (this.status != OrderStatus.DRAFT) {
throw new OrderException("Only draft orders can add items");
}
this.items.add(new OrderItem(productId, unitPrice, quantity));
}
public void place() {
if (this.items.isEmpty()) {
throw new OrderException("Order must have at least one item");
}
this.totalAmount = items.stream()
.map(OrderItem::subtotal)
.reduce(Money::add)
.orElseThrow();
addDomainEvent(new OrderPlacedEvent(this.id, this.totalAmount));
}
public void pay() {
if (!this.status.canPay()) {
throw new OrderException("Cannot pay order in status: " + this.status);
}
this.status = OrderStatus.PAID;
addDomainEvent(new OrderPaidEvent(this.id));
}
public void cancel(String reason) {
if (!this.status.canCancel()) {
throw new OrderException("Cannot cancel order in status: " + this.status);
}
this.status = OrderStatus.CANCELLED;
addDomainEvent(new OrderCancelledEvent(this.id, reason));
}
}从本案例学到的 DDD 关键点
1. 聚合设计:Order 聚合只包含紧密相关的 OrderItem,Product 和 Member 通过 ID 引用 2. 值对象:Money、OrderStatus、Address 都是不可变值对象,自带业务行为 3. 领域事件:状态变更通过领域事件传播,实现聚合间最终一致性 4. 充血模型:place()、pay()、cancel() 业务方法在聚合根上,不是 Service 5. 防腐层:外部上下文数据通过 ACL 转换,不污染本上下文模型
保险投保系统 — DDD 聚合设计深度案例
基于 DDD 的保险投保系统,重点展示复杂聚合的设计方法。
业务背景
保险投保核心流程:
- 用户创建投保单 → 选择险种 → 填写标的信息 → 填入被保人 → 核保 → 缴费 → 出单
- 涉及多个参与方:投保人、被保人、受益人
- 复杂的业务规则:年龄校验、健康告知、保额计算、费率表
领域划分
| 域 | 类型 | 说明 |
|---|---|---|
| 投保域 | Core Domain | 投保流程、核保规则,核心竞争优势 |
| 产品域 | Supporting | 险种定义、费率表,相对稳定 |
| 核保域 | Core Domain | 风险评估、健康告知,核心决策能力 |
| 支付域 | Generic | 通用支付能力 |
限界上下文
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Proposal BC │────→│ Underwrite BC│────→│ Reinsure BC │
│ (投保上下文) │ │ (核保上下文) │ │ (再保上下文) │
└──────┬───────┘ └──────┬───────┘ └──────────────┘
│ │
↓ ↓
┌──────────────┐ ┌──────────────┐
│ Product BC │ │ Payment BC │
│ (产品上下文) │ │ (支付上下文) │
└──────────────┘ └──────────────┘聚合设计 — 重点
投保单聚合(Proposal Aggregate)
这是一个典型的复杂聚合,包含多个实体和值对象:
Proposal (聚合根)
├── ProposalId (VO)
├── ProposalStatus (VO: DRAFT → SUBMITTED → UNDERWRITING → APPROVED → PAID → ISSUED)
├── Applicant (VO: 投保人信息)
│ ├── Name, IDNumber, Phone, Address
├── List<Insured> (Entity: 被保人)
│ ├── InsuredId
│ ├── Name, IDNumber, Age, Gender
│ ├── HealthDeclaration (VO: 健康告知)
│ └── Occupation (VO)
├── List<Coverage> (Entity: 险种)
│ ├── ProductId (VO — 外部引用)
│ ├── Money sumInsured (VO: 保额)
│ └── Money premium (VO: 保费)
├── List<Beneficiary> (VO: 受益人 — 不可变,无独立生命周期)
├── ── 行为 ──
├── void addInsured(Insured)
├── void addCoverage(ProductId, Money sumInsured)
├── void calculatePremium(RateTable) // 保费计算
├── void submit() // 提交核保
├── void approve() // 核保通过
├── void reject(String reason) // 核保拒绝
└── ── 事件 ──
├── ProposalSubmittedEvent
├── UnderwritingApprovedEvent
└── PolicyIssuedEvent聚合设计决策分析
| 问题 | 决策 | 理由 |
|---|---|---|
| Insured 是实体还是值对象? | Entity | 有独立 ID,会被多次引用 |
| Beneficiary 是实体还是值对象? | VO | 无独立生命周期,随投保单变化 |
| Coverage 放在哪个聚合? | Proposal 内 | 保额、保费与投保单强一致 |
| Product 怎么引用? | 仅通过 ProductId | 产品自身变更不应影响已有投保单 |
| 核保结果怎么通知? | 领域事件 | 核保是独立 BC,异步通知结果 |
聚合大小分析
| 指标 | 值 | 评估 |
|---|---|---|
| 每聚合实体数 | 3 (Insured, Coverage — 实体; Beneficiary — VO) | ✅ 健康 |
| 聚合根代码行数 | ~200 | ✅ 健康 |
| 事务锁时间 | <50ms | ✅ 健康 |
为什么 Coverage 放在 Proposal 而非独立聚合?
1. 一致性要求:保额和保费的修改必须和投保单状态保持强一致 2. 不变量:投保单提交时,所有 Coverage 的 premium 总和必须等于 totalPremium 3. 生命周期:Coverage 随投保单创建和销毁,无独立生命周期
领域服务
// PremiumCalculator — 保费计算(跨实体逻辑,不适合放在聚合根或实体中)
public class PremiumCalculator {
public Money calculate(Insured insured, ProductId product, Money sumInsured, RateTable rateTable) {
// 1. 根据被保人年龄查费率
BigDecimal rate = rateTable.findRate(insured.getAge(), product);
// 2. 根据职业类别调整系数
BigDecimal occupationFactor = insured.getOccupation().getFactor();
// 3. 计算保费 = 保额 × 费率 × 职业系数
return sumInsured.multiply(rate).multiply(occupationFactor);
}
}分层架构代码示例
// Domain Layer — 聚合根
public class Proposal extends AggregateRoot<ProposalId> {
private Applicant applicant;
private List<Insured> insureds = new ArrayList<>();
private List<Coverage> coverages = new ArrayList<>();
private List<Beneficiary> beneficiaries = new ArrayList<>();
private ProposalStatus status = ProposalStatus.DRAFT;
public void addCoverage(ProductId productId, Money sumInsured, RateTable rateTable) {
if (status != ProposalStatus.DRAFT) {
throw new ProposalException("Only draft proposals can modify coverage");
}
Coverage coverage = new Coverage(productId, sumInsured);
for (Insured insured : insureds) {
Money premium = new PremiumCalculator()
.calculate(insured, productId, sumInsured, rateTable);
coverage.addPremium(insured.getId(), premium);
}
this.coverages.add(coverage);
}
public void submit() {
if (insureds.isEmpty()) throw new ProposalException("At least one insured required");
if (coverages.isEmpty()) throw new ProposalException("At least one coverage required");
this.status = ProposalStatus.SUBMITTED;
addDomainEvent(new ProposalSubmittedEvent(this.id));
}
}从本案例学到的 DDD 关键点
1. 聚合边界决策:不是越细越好,要根据一致性要求和不变量来判断 2. 实体 vs 值对象:关键在于是否有独立标识和生命周期 3. 领域服务:跨实体的计算逻辑(如保费计算)放入领域服务,保持实体纯粹 4. 聚合大小权衡:3 个实体是健康范围,超出 5 个实体需要警惕 5. ID 引用外部聚合:Product 只用 ProductId 引用,避免跨聚合直接依赖
Why DDD — 领域驱动设计的价值与适用场景
DDD 核心定义
DDD 是一种处理高度复杂领域的设计方法论,通过边界划分将复杂业务领域简单化,建立清晰的领域和应用边界。
DDD 不是架构,而是架构设计方法论。它由两部分组成:
| 部分 | 视角 | 产出 |
|---|---|---|
| 战略设计 | 业务视角 | 领域模型、限界上下文、通用语言 |
| 战术设计 | 技术视角 | 聚合根、实体、值对象、领域服务、仓储等代码实现 |
DDD 与微服务的关系
| 维度 | DDD | 微服务 |
|---|---|---|
| 本质 | 架构设计方法 | 架构风格 |
| 关注点 | 业务边界、通用语言、领域模型 | 进程通信、容错、独立部署 |
| 关系 | DDD 的限界上下文 ≈ 微服务边界 | 微服务以 DDD 的边界为依据拆分 |
核心公式: 战略设计划定边界 → 限界上下文映射为微服务 → 战术设计实现代码
DDD 适用性评估
三步决策法
1. 业务逻辑是否复杂(规则、状态机、约束)?
├── NO → 检查系统生命周期
│ ├── < 2 年 → ❌ 不建议 DDD,用简单 CRUD + Service
│ └── > 2 年 → ~ 谨慎采用,选择性使用战术模式
└── YES → 2. 是否有领域专家或稳定的产品负责人?
├── NO → ~ 谨慎:没有领域专家的 DDD = 为虚无建模
└── YES → 3. 团队是否熟悉 DDD 或愿意学习?
├── NO → ~ 从分层架构开始,逐步演进
└── YES → ✓ DDD 推荐适用场景判断表
| 维度 | 适用 ✓ | 谨慎 ~ | 不推荐 ✗ |
|---|---|---|---|
| 业务复杂度 | 复杂规则、状态机、跨团队协作 | 部分业务规则 | 纯 CRUD |
| 生命周期 | 长期持续演进 | 中期迭代 | 短期一次性 |
| 团队成熟度 | 有专职领域专家 | 通用业务知识 | 无领域知识 |
| 工程能力 | 强测试/可观测/事件驱动 | 基本 CI/CD | 无测试基础设施 |
| 团队规模 | 5+ 人 | 3-5 人 | 1-2 人 |
DDD 带来的核心价值
| 价值 | 说明 |
|---|---|
| 标准化设计过程 | 从战略到战术的完整方法论,设计思路清晰 |
| 复杂业务建模 | 建立核心稳定的领域模型,利于知识传承 |
| 团队协作 | 统一语言促进领域专家与开发者沟通 |
| 架构演进 | 边界清晰,容易从单体演进到微服务 |
| 适用范围广 | 适用于微服务、中台、单体应用 |
三层边界划分法
第一层:聚合边界(逻辑边界)
└── 将紧密关联的实体和值对象组合为聚合,在一个微服务实例内运行
第二层:限界上下文边界(物理边界)
└── 一个或多个聚合划定在限界上下文内,映射为独立微服务
第三层:域边界
└── 按核心域/支撑域/通用域分类,分配不同的资源投入策略避免的误区
| 误区 | 正确做法 |
|---|---|
| DDD = COLA 框架 | COLA 只是一种落地方案,还有 Layered/Onion/Hexagonal/Clean |
| 一上来就 CQRS + Event Sourcing | 从最简单的分层架构开始,逐步演进 |
| DDD 是银弹 | 仅复杂业务领域适用,简单 CRUD 是过度设计 |
| 战术设计先于战略设计 | 先划 Bounded Context,再写 Entity/ValueObject |
DDD 的 5 级演进阶梯
Level 1: Simple Layered (Controller → Service → Repository)
↓ 业务规则变复杂
Level 2: DDD 4-Layer with Rich Domain Model
↓ 需要多入口 (REST + CLI + MQ + gRPC)
Level 3: Ports & Adapters (Hexagonal Architecture)
↓ 读写模式显著分化
Level 4: CQRS — Separate Read/Write Models
↓ 需要完整审计追踪 / 时间旅行查询
Level 5: Event Sourcing — Store events, derive state原则: 不要跨级跳跃。每个级别增加真实复杂度,只有在证明当前级别不够时才升级。
Domain & Subdomain — 领域与子域划分
核心概念
| 概念 | 定义 | 类比 |
|---|---|---|
| 领域 (Domain) | 组织从事的业务范围,整个问题域 | 整个公司业务 |
| 子域 (Subdomain) | 领域细分的子问题域 | 公司内的部门 |
| 限界上下文 | 子域内语义一致的模型边界 | 部门内的职责范围 |
三类子域
| 类型 | 定义 | 投入策略 | 示例 |
|---|---|---|---|
| 核心域 (Core) | 决定产品核心竞争力的子域 | 最大投入,自主研发 | 电商:订单、支付 |
| 支撑域 (Supporting) | 必需的但非核心竞争力 | 定制开发或外购 | 数据字典、报表 |
| 通用域 (Generic) | 通用功能,无企业特性 | 购买或使用开源 | 认证、权限、通知 |
如何识别核心域
问三个问题:
1. 这个功能是否是公司的核心竞争力?
2. 用户是否因为这个功能选择我们而非竞品?
3. 如果去掉这个功能,业务是否还能运转?
三个都是 YES → Core Domain
部分是 YES → Supporting
全部是 NO → Generic资源分配策略
核心域:>60% 资源 → 充血模型 + DDD + 最佳团队
支撑域:20-30% 资源 → 简单 DDD 或外购
通用域:<10% 资源 → 购买/开源 + 最小定制领域细分流程
4 步分解法
Step 1: 确定研究领域
└── 明确要解决的业务问题域范围
Step 2: 一级细分 → 子域
└── 按业务职能分组(如保险 → 承保、理赔、收付、再保)
Step 3: 二级细分 → 子子域
└── 按流程边界细分(如承保 → 投保、保全、批改)
Step 4: 识别限界上下文
└── 语义边界 = 限界上下文边界
└── 一个限界上下文 ≈ 一个微服务以保险行业为例
保险领域
├── 承保子域 (Core)
│ ├── 投保 (Bounded Context → 微服务)
│ ├── 保全/批改 (Bounded Context → 微服务)
│ └── 核保 (Bounded Context → 微服务)
├── 理赔子域 (Core)
│ ├── 报案 (Bounded Context → 微服务)
│ ├── 查勘 (Bounded Context → 微服务)
│ └── 定损理算 (Bounded Context → 微服务)
├── 收付子域 (Supporting)
│ └── 收付 (Bounded Context → 微服务)
└── 再保子域 (Supporting)
└── 再保 (Bounded Context → 微服务)以电商行业为例
电商领域
├── 交易子域 (Core) → 订单、购物车
├── 商品子域 (Supporting) → 商品管理、类目
├── 会员子域 (Generic) → 注册、登录、积分
├── 营销子域 (Core) → 优惠券、促销活动
├── 物流子域 (Supporting) → 发货、签收
└── 支付子域 (Supporting) → 支付、退款领域细分与微服务拆分的关系
| 层级 | 领域概念 | 微服务映射 |
|---|---|---|
| 领域 | 整个业务范围 | 一个或多个微服务系统 |
| 子域 | 业务职能分组 | 微服务组 |
| 限界上下文 | 语义一致的模型边界 | 一个微服务 |
| 聚合 | 一致性边界 | 微服务内部模块 |
常见错误
| 错误 | 纠正 |
|---|---|
| 所有子域同等对待 | 核心域重点投入,通用域购买/复用 |
| 子域拆分过细 | 一个子域可有多个限界上下文,不要盲目拆微服务 |
| 领域专家不参与 | 核心域划分必须由领域专家主导,不能纯技术驱动 |
| 领域划分一成不变 | 随业务演进调整,核心域可能变为通用域 |
Bounded Context — 限界上下文与通用语言
核心概念
| 概念 | 定义 | 作用 |
|---|---|---|
| 通用语言 (Ubiquitous Language) | 团队统一的业务术语和表达方式 | 消除歧义,领域专家与开发者用同一套语言 |
| 限界上下文 (Bounded Context) | 通用语言的语义边界 | 确保同一术语在边界内含义唯一 |
| 上下文映射 (Context Mapping) | 限界上下文之间的关系模式 | 指导上下文间如何集成 |
通用语言
构建流程
事件风暴 → 团队协商 → 形成统一术语 → 映射到代码语言映射规则
| 词性 | 来源 | 映射到 |
|---|---|---|
| 名词 | 业务对象名称 | 实体/值对象类名、属性名 |
| 动词 | 业务动作 | 领域事件、命令、方法名 |
| 形容词 | 业务状态 | 状态枚举、值对象 |
代码示例
// ✅ 通用语言直接映射到代码
public class Order { // 名词:订单
public void pay(PaymentMethod method) {} // 动词:支付
public void cancel(String reason) {} // 动词:取消
}
// 领域事件命名:OrderPaid, OrderCancelled(过去式)限界上下文
边界划分流程
1. 事件风暴梳理所有领域对象
2. 按语义一致性聚类对象
3. 识别同一术语在不同语境中的含义差异
4. 划分语义边界 → 即限界上下文
5. 一个限界上下文 ≈ 一个微服务边界识别信号
| 信号 | 说明 | 行动 |
|---|---|---|
| 同一术语含义不同 | 如 "商品" 在销售 vs 运输中的不同含义 | 划分不同 BC |
| 团队组织边界 | 不同团队维护不同部分 | 对齐 BC 边界 |
| 数据变更频率差异 | 核心数据频繁变,配置数据很少变 | 分开 BC |
| 技术异构需求 | 不同部分需要不同技术栈 | 分开 BC |
示例:电商术语边界
Product BC(商品上下文):
"商品" = 带有标题、价格、SKU 的可售卖物品
Order BC(订单上下文):
"商品" = 订单中的行项目(快照,价格已固定)
Logistics BC(物流上下文):
"商品" = 需要运输的物理货物(货物≠商品)同一件东西,在不同 BC 中术语和含义不同。每个 BC 内的术语含义唯一。
示例:保险术语边界
| BC | 核心术语 | 含义 |
|---|---|---|
| 投保 BC | 投保单 | 客户的投保申请(DRAFT 状态) |
| 承保 BC | 保单 | 已生效的保险合同(ACTIVE 状态) |
| 保全 BC | 批单 | 保单的变更记录 |
| 理赔 BC | 赔案 | 出险后的理赔案件 |
上下文映射模式
| 模式 | 描述 | 使用场景 |
|---|---|---|
| Partnership | 两个团队合作,共同演进 | 同一组织内紧密协作的 BC |
| Shared Kernel | 共享部分模型 | 重叠的领域模型,需严格控制变更 |
| Customer-Supplier | 上游提供,下游消费 | 上下游关系明确(如订单→支付) |
| Conformist | 下游完全跟随上游 | 下游无力影响上游设计 |
| ACL (Anti-Corruption Layer) | 翻译层,隔离外部模型 | 对接遗留系统、第三方服务 |
| OHS (Open Host Service) | 公开 API,多消费者 | 需要服务多个下游 BC |
| Published Language | 标准数据格式 | 跨团队共享的数据模型(XML/JSON Schema) |
| Separate Ways | 不集成,各自独立 | 集成成本高于收益 |
选择流程图
两个 BC 需要交互吗?
├── NO → Separate Ways
└── YES → 谁主导?
├── 平等合作 → Partnership 或 Shared Kernel
├── 上游主导 → Customer-Supplier
└── 下游只能跟随 → Conformist
是否对接外部/遗留系统?
├── YES → ACL (Anti-Corruption Layer)
└── NO → OHS (Open Host Service) + Published Language限界上下文 → 微服务映射
限界上下文(逻辑边界)
│
▼ 映射(考虑以下因素)
│
微服务(物理边界)
考虑因素:
├── 团队规模与组织结构(康威定律)
├── 技术异构需求
├── 独立部署频率
├── 数据一致性要求
└── 性能与弹性需求原则: 理论上一个 BC ≈ 一个微服务,但实际需综合考虑。一个 BC 可拆为多个微服务,多个 BC 也可合并为一个微服务。
常见错误
| 错误 | 纠正 |
|---|---|
| BC 边界与团队边界不对齐 | 按康威定律,组织结构决定系统结构 |
| 通用语言只在文档中存在 | 必须直接映射到代码(类名、方法名、事件名) |
| 同一术语跨 BC 混用 | 每个 BC 独立维护自己的术语表 |
| BC 拆分过细 | 不要为一个微服务拆一个 BC,首先看语义边界 |
Entity & Value Object — 实体与值对象
对比速查
| 维度 | 实体 (Entity) | 值对象 (Value Object) |
|---|---|---|
| 标识 | 有唯一 ID | 无 ID |
| 相等性 | 通过 ID 判断 | 通过所有属性值判断 |
| 可变性 | 可变 | 不可变 |
| 生命周期 | 独立生命周期 | 无独立生命周期,用完即扔 |
| 持久化 | 通常持久化 | 嵌入实体或序列化 |
| 行为 | 丰富的业务行为 | 数据初始化 + 整体替换 |
| 关系 | 引用其他实体和值对象 | 尽量只引用值对象 |
实体
四种形态
| 形态 | 说明 | 示例 |
|---|---|---|
| 业务形态 | 事件风暴中的领域对象 | Order, Customer |
| 代码形态 | 实体类(充血模型) | class Order { void pay() {} } |
| 运行形态 | DO(领域对象),有唯一 ID | Order(id=123, status=PAID) |
| 数据库形态 | 1 个实体 → 0/1/多个持久化对象 | Order → orders 表 + order_items 表 |
代码模板
// 充血模型:行为在实体内部
public class Order extends AggregateRoot<OrderId> {
private OrderId id;
private OrderStatus status;
private Money totalAmount;
public void pay() {
if (!status.canPay()) {
throw new OrderException("Cannot pay order in " + status);
}
this.status = OrderStatus.PAID;
addDomainEvent(new OrderPaidEvent(this.id));
}
}实体与持久化对象的映射
| 场景 | 映射关系 | 说明 |
|---|---|---|
| 常规 | 1:1 | 一个实体对应一张表 |
| 纯内存实体 | 1:0 | 如折扣实体,基于多个价格配置计算生成 |
| 复合实体 | 1:N | 如权限实体 = user 表 + role 表 |
| 宽表实体 | N:1 | 如 Customer + Account 共享一张宽表 |
值对象
设计原则
值对象 = 无 ID + 不可变 + 概念完整的属性集合
设计检查:
1. 这个对象是否由属性定义(而非身份)?
2. 修改这个对象的属性是否意味着它是一个不同的东西?
3. 两个实例所有属性相同,是否应该被视为相等?
全部 YES → Value Object代码示例
// ✅ 不可变值对象
public record Money(BigDecimal amount, Currency currency) {
public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new IllegalArgumentException("Currency mismatch");
}
return new Money(this.amount.add(other.amount), this.currency);
}
}
// ✅ 不可变值对象
public record Address(String province, String city, String district, String street) {
// 无 setter,无 ID
}两种代码形态
| 形态 | 适用场景 | 示例 |
|---|---|---|
| 单一属性 | 简单值 | String name |
| 属性集合 (Class) | 多个相关属性 | Address address |
两种持久化方式
| 方式 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 属性嵌入 | 值对象字段直接作为实体表列 | 查询方便 | 实体表列数增多 |
| 序列化大对象 | 值对象序列化为 JSON 存入一个字段 | 表结构简化 | 无法查询值对象内部字段 |
示例对比
-- 方式 1: 属性嵌入
CREATE TABLE person (
id BIGINT PRIMARY KEY,
name VARCHAR(50),
province VARCHAR(20), -- address.province
city VARCHAR(20), -- address.city
district VARCHAR(20), -- address.district
street VARCHAR(100) -- address.street
);
-- 方式 2: 序列化大对象
CREATE TABLE person (
id BIGINT PRIMARY KEY,
name VARCHAR(50),
address JSON -- {"province":"...","city":"...","district":"...","street":"..."}
);实体 vs 值对象 — 决策流程
这是一个领域对象。问:
1. 它需要唯一标识,且标识在其生命周期内保持不变吗?
├── YES → 2. 它的状态会随时间改变吗?
│ ├── YES → Entity
│ └── NO → 3. 它是否描述了另一个对象?
│ ├── YES → Value Object
│ └── NO → Entity(不可变实体)
└── NO → Value Object
快速判断:
"这是 THE 同一个东西吗?" → Entity(身份比较)
"这和那个有相同的值吗?" → Value Object(结构相等)领域建模 vs 数据建模
传统方式(数据建模优先):
设计表 → 设计实体 → 每个表一个实体 → 主表关联从表 → 表爆炸
DDD 方式(领域建模优先):
领域建模 → 识别 Entity 和 VO → VO 嵌入实体 → 减少表数量 → 简化数据库| 对比维度 | 数据建模优先 | 领域建模优先 (DDD) |
|---|---|---|
| 起点 | 数据库表 | 业务领域 |
| 实体粒度 | 每表一实体 | 按业务聚合 |
| 表数量 | 多(范式化) | 少(VO 嵌入) |
| 业务表达 | 被数据模型限制 | 领域模型主导 |
常见错误
| 错误 | 纠正 |
|---|---|
| 值对象加了 ID 和 setter | 值对象 = 无 ID + 不可变 |
| 把 Address、Money 设计成 Entity | 属性相同即相等 → VO |
| 值对象嵌套值对象有 setter | 整体替换,不要局部修改 |
| Entity 只有 getter/setter | 充血模型:行为在 Entity 内部 |
| 实体 ID 用数据库自增 | 使用 UUID 或业务编号 |
Aggregate Design — 聚合设计
聚合定义
聚合 = 业务和逻辑紧密关联的实体和值对象的组合,是数据修改和持久化的基本单元。
| 要素 | 说明 |
|---|---|
| 聚合根 (Aggregate Root) | 聚合的唯一入口,负责协调内部对象并对外接口 |
| 实体 (Entity) | 有 ID,状态可变 |
| 值对象 (Value Object) | 无 ID,不可变,被聚合根或实体引用 |
| 边界 | 根据业务单一职责和高内聚原则定义 |
为什么需要聚合
| 问题 | 聚合如何解决 |
|---|---|
| 实体间无控制调用导致数据不一致 | 聚合根统一控制所有变更 |
| 跨实体业务规则分散 | 聚合根封装不变量 |
| 外部直接访问内部实体 | 聚合根作为唯一入口,外部不能绕过 |
| 事务边界模糊 | 一个聚合 = 一个事务边界 |
5 步构建聚合
Step 1: 事件风暴 → 梳理所有实体和值对象
产出:实体列表(投保单、标的、客户、被保人等)
Step 2: 选出聚合根
判断标准:
├── 是否有独立的生命周期?
├── 是否有全局唯一 ID?
├── 是否可以创建或修改其它对象?
└── 是否有专门的模块管理?
Step 3: 按单一职责和高内聚原则,找出与聚合根紧密依赖的实体和值对象
产出:聚合内对象集合
Step 4: 画引用和依赖模型
规则:聚合间通过聚合根 ID 关联,不直接引用对象
Step 5: 多个聚合按业务语义归入同一限界上下文聚合设计 6 原则
| # | 原则 | 说明 | 违反后果 |
|---|---|---|---|
| 1 | 一致性边界内建模真正的不变条件 | 聚合封装不变的业务规则 | 数据不一致 |
| 2 | 设计小聚合 | 实体 > 5 有 N+1 隐患 | 并发冲突、锁等待 |
| 3 | 通过唯一标识引用其他聚合 | 聚合间 ID 关联,非对象引用 | 边界模糊、耦合增加 |
| 4 | 边界之外使用最终一致性 | 一次事务只改一个聚合 | 长事务、分布式锁 |
| 5 | 通过应用层实现跨聚合服务调用 | 避免跨聚合领域服务调用 | 跨聚合耦合 |
| 6 | 适合自己才是最好的 | 可突破原则,实际场景优先 | 教条主义 |
聚合大小度量
| 指标 | 健康 | 警告 | 需行动 |
|---|---|---|---|
| 每聚合实体数 | 1-5 | 6-10 | >10: 拆分 |
| 聚合根代码行数 | <500 | 500-1000 | >1000: 拆分 |
| 事务锁时间 | <100ms | 100-500ms | >500ms: 拆分 |
聚合根职责
聚合根 = 实体 + 管理者 + 对外接口
作为实体:
└── 有自己的属性和业务行为
作为管理者:
└── 协调聚合内实体和值对象按固定规则协同工作
作为对外接口:
└── 聚合间通过聚合根 ID 关联,外部只能通过聚合根访问内部跨聚合交互规则
正确做法:
Order.pay() → 生成 OrderPaidEvent
Inventory.deduct(OrderPaidEvent) → 异步响应
错误做法:
OrderService.pay() {
order.setStatus(PAID); // 直接修改状态
inventory.deduct(itemId); // 跨聚合同步调用
}常见错误
| 错误 | 纠正 |
|---|---|
| 聚合过大(10+ 实体) | 用 ID 引用替代对象引用,拆分为小聚合 |
| 一个事务修改多个聚合 | 最多改一个聚合,跨聚合用领域事件最终一致 |
| 外部直接访问聚合内部实体 | 必须通过聚合根,聚合根是唯一入口 |
| 聚合根 ID 用数据库自增 ID | 使用 UUID 或业务编号,保证分布式唯一 |
| 聚合间对象引用 | 聚合间只通过聚合根 ID 关联 |
Aggregate Deep Dive — 聚合设计深度指南
聚合设计 6 原则详解
原则 1:在一致性边界内建模真正的不变条件
不变量 = 无论发生什么操作都必须保持的业务规则
示例:Order 聚合的不变量
├── 订单总金额 = 所有 OrderItem.subtotal 之和
├── PAID 状态的订单不能修改商品
├── CANCELLED 状态的订单不能再支付
└── 同一订单中不能有重复的 SKU方法: 在聚合根的方法中检查不变量,失败抛异常。
public void pay() {
if (this.status != OrderStatus.DRAFT) {
throw new OrderException("Only DRAFT orders can be paid");
}
this.status = OrderStatus.PAID;
}原则 2:设计小聚合
聚合大小 = 直接影响并发性能
过大的后果:
├── 事务锁范围大 → 并发冲突增加
├── 加载全部关联数据 → N+1 查询
├── 修改频率不一致的数据耦合 → 无意义锁等待
└── 重构困难 → 牵一发动全身拆分决策流程:
当前聚合是否过大?
├── 两个实体是否总是同时修改?
│ ├── 是 → 保留在同一聚合
│ └── 否 → 考虑拆分
├── 聚合内有实体可以独立存在吗?
│ ├── 是 → 拆分为独立聚合
│ └── 否 → 保持
└── 聚合内实体数 > 5?
├── 是 → 强烈建议拆
└── 否 → 保持原则 3:通过唯一标识引用其他聚合
// ✅ 正确:ID 引用
public class Order {
private BuyerId buyerId; // VO,包装 Member BC 的 ID
private List<OrderItem> items;
}
// ❌ 错误:对象引用
public class Order {
private Member buyer; // 直接引用了外部聚合
}原则 4:在边界之外使用最终一致性
| 场景 | 方案 |
|---|---|
| 订单支付成功 → 扣减库存 | 领域事件:OrderPaid → InventoryDeducted |
| 订单取消 → 释放库存 | 领域事件:OrderCancelled → InventoryReleased |
| 订单签收 → 发放积分 | 领域事件:OrderDelivered → PointsAwarded |
原则 5:通过应用层实现跨聚合服务调用
聚合 A ──事件──→ 聚合 B ✅ 领域事件解耦
应用服务协调聚合 A + 聚合 B ✅ 编排层
聚合 A 直接调聚合 B 的领域服务 ❌ 跨聚合耦合原则 6:适合自己才是最好的
可突破场景:
- 高性能要求 → 可适当放宽一致性约束
- 团队能力限制 → 可先采用简单设计再演进
- 全局事务需求 → 可评估 Saga 或 TCC 方案
聚合根选择指南
候选实体评估:
1. 是否有独立的生命周期?
例:Order 有 DRAFT→PAID→SHIPPED→DELIVERED 完整生命周期
2. 是否有全局唯一标识?
例:Order 有 orderId,全局唯一
3. 是否负责创建或修改其他实体?
例:Order 管理 OrderItem 的添加和移除
4. 是否有专门的模块或团队管理?
例:有专门的订单模块/订单团队
满足 ≥ 2 个条件 → 适合作为聚合根大聚合拆分案例
拆分前(问题聚合)
电商核心聚合(过大):
├── Product (聚合根)
│ ├── SKU (Entity)
│ ├── Category (Entity)
│ ├── Inventory (Entity)
│ ├── Price (Entity)
│ └── Review (Entity) ← 可以和 Product 分开拆分后
Product 聚合:
├── Product (聚合根)
├── SKU (Entity)
└── Price (Value Object)
Inventory 聚合:
├── Inventory (聚合根)
└── WarehouseLocation (Value Object)
Review 聚合:
├── Review (聚合根)
└── Rating (Value Object)
关联方式:
Product.id → Inventory.productId (ID 引用)
Product.id → Review.productId (ID 引用)Domain Events — 领域事件与事件驱动设计
定义与识别
| 维度 | 说明 |
|---|---|
| 定义 | 领域中发生的有意义的事件,触发进一步的业务操作 |
| 命名 | 过去式:OrderPaid, UserCreated, ShipmentDelivered |
识别关键词
用户/专家口中出现这些句式时 = 领域事件:
├── "当...发生时,则..." → 支付完成时,生成保单
├── "做完...时,通知..." → 创建用户后,发送邮件
├── "发生...时,则..." → 密码输错3次,锁定账户
└── "如果...,那么..." → 如果审批通过,则通知申请人为什么用事件而非直接调用
| 方式 | 一致性 | 耦合度 | 适用 |
|---|---|---|---|
| 直接服务调用 | 强一致 | 高耦合 | 实时要求高、同聚合内 |
| 领域事件 | 最终一致 | 低耦合 | 跨聚合、跨微服务 |
核心原则:一次事务最多修改一个聚合。跨聚合用领域事件实现最终一致性。
事件处理架构
事件构建 → 事件持久化 → 事件总线/消息中间件 → 订阅方接收 → 业务处理事件实体设计
public abstract class DomainEvent {
private String eventId; // 全局唯一ID(UUID)
private LocalDateTime occurredOn; // 发生时间
private String eventType; // 事件类型
private String sourceAggregate; // 事件源聚合
// 业务属性 — 事件发生时的数据快照(不可变,序列化为值对象)
}事件基本属性
| 属性 | 说明 |
|---|---|
| eventId | 全局唯一,跨限界上下文传递,用于幂等去重 |
| occurredOn | 事件发生的时间戳 |
| eventType | 事件类型标识 |
| sourceAggregate | 哪个聚合触发的 |
| 业务属性 | 事件发生时的业务数据快照(序列化值对象形式) |
持久化方案
| 方案 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 本地事件表 | 业务和事件同库 | 本地事务保证一致性 | 需定时抽取增量到消息中间件 |
| 共享事件库 | 事件独立数据库 | 事件集中管理 | 需分布式事务 |
发布方式
| 方式 | 适用场景 |
|---|---|
| 应用服务/领域服务直接发布 | 简单场景 |
| 定时程序从事件表抽取发布 | 允许短暂延迟 |
| 数据库 CDC (Debezium等) | 实时性要求高 |
同步/异步决策
| 场景 | 方案 |
|---|---|
| 同微服务内聚合间,需强一致 | 应用服务编排 + 同步调用 |
| 同微服务内聚合间,可异步 | EventBus |
| 跨微服务 | 消息中间件 (Kafka/RabbitMQ) + 异步 |
跨微服务事件示例
投保微服务 收款微服务 保单微服务
───────── ───────── ─────────
生成缴费通知单
│
├─发布→ 缴费通知单已生成 → 订阅收费
│ │
│ 缴费完成
│ │
│←订阅← 缴费已完成 ←────────发布
│
投保单→保单
│
├─发布→ 保单已生成 ──────────────────────────→ 订阅保存保单事件总线 (EventBus)
微服务内的事件分发组件:
事件发布 → EventBus
├── 微服务内订阅者 → 同步/异步分发到指定聚合
├── 微服务外订阅者 → 持久化到事件表 → 异步发消息中间件
└── 两者都有 → 先发内部,再持久化,再异步发外部幂等性设计
// 消费者必须实现幂等:同一事件可能被投递多次
public void handle(OrderPaidEvent event) {
if (eventStore.exists(event.getEventId())) {
return; // 重复事件,跳过
}
// 处理业务逻辑
eventStore.save(event.getEventId());
}常见错误
| 错误 | 纠正 |
|---|---|
| 用领域事件替代所有服务调用 | 同聚合内强一致场景用同步调用 |
| 事件不幂等 | 消费者必须做去重处理 |
| 事件携带超大业务数据 | 事件只带关键标识,详情查源 |
| 事件结构向后不兼容 | 只增字段,不删不改;用 Avro/Protobuf |
| 忘记持久化事件 | 事件必须先持久化再发布,防丢失 |
DDD Building Blocks 与命名约定
聚合大小度量表
| 指标 | 健康 | 警告 | 行动 |
|---|---|---|---|
| 每聚合实体数 | 1-5 | 6-10 | >10: 拆分 |
| 聚合根代码行数 | <500 | 500-1000 | >1000: 拆分 |
| 事务锁时间 | <100ms | 100-500ms | >500ms: 拆分 |
| 并发修改冲突 | 极少 | 偶尔 | 频繁: 拆分 |
文件命名约定
domain/
├── order/
│ ├── Order.java # 聚合根
│ ├── OrderItem.java # 实体
│ ├── value_objects.java # OrderId, Money, OrderStatus
│ ├── events.java # OrderCreated, OrderPaid, OrderShipped
│ ├── repository.java # OrderRepository(接口)
│ ├── services.java # 领域服务
│ └── errors.java # OrderException
application/
├── place_order/
│ ├── command.java # PlaceOrderCommand
│ ├── handler.java # PlaceOrderHandler
│ └── port.java # IPlaceOrderUseCase
infrastructure/
├── postgres/
│ ├── order_repository.java # PostgresOrderRepository
│ └── mappers/
│ └── order_mapper.java # Domain ↔ DB 映射六边形端口命名约定
| 类型 | 模式 | 示例 |
|---|---|---|
| Driver Port | I{Action}UseCase | IPlaceOrderUseCase, IGetOrderUseCase |
| Driven Port | I{Resource}Repository | IOrderRepository, IProductRepository |
| Driven Port | I{Action}Service | IPaymentService, INotificationService |
| Driven Port | I{Resource}Gateway | IPaymentGateway, IShippingGateway |
文件命名约定(TypeScript 版)
domain/
├── order/
│ ├── order.ts # 聚合根
│ ├── order_item.ts # 实体
│ ├── value_objects.ts # OrderId, Money 等
│ ├── events.ts # OrderCreated 等
│ ├── repository.ts # IOrderRepository
│ ├── services.ts # 领域服务
│ └── errors.ts # OrderError 等
application/
├── place_order/
│ ├── command.ts # PlaceOrderCommand
│ ├── handler.ts # PlaceOrderHandler
│ └── port.ts # IPlaceOrderUseCase
infrastructure/
├── postgres/
│ ├── order_repository.ts # PostgresOrderRepository
│ └── mappers/
│ └── order_mapper.ts # Domain ↔ DB 映射依赖规则矩阵
| Domain | Application | Infrastructure | |
|---|---|---|---|
| Domain | ✅ | ❌ | ❌ |
| Application | ✅ | ✅ | ❌ |
| Infrastructure | ✅ | ✅ | ✅ |
✅ = 可以依赖 ❌ = 不可以依赖
复杂度阶梯速查
Level 1: Simple layered(Controller → Service → Repository)
↓ 业务规则变得复杂
Level 2: Domain model(Entities with behavior)
↓ 需要多个入口
Level 3: Hexagonal(Ports & Adapters)
↓ 读写模式显著分化
Level 4: CQRS(Separate read/write models)
↓ 需要完整审计追踪
Level 5: Event Sourcing(Store events, derive state)
不要跳级。每一级增加真实的复杂度。只在证明当前级别不够时才升级。Entity vs Value Object 速判
Entity or Value Object?
├─ Has unique identity that persists → Entity
├─ Defined only by its attributes → Value Object
├─ "Is this THE same thing?" → Entity (identity comparison)
└─ "Does this have the same value?" → Value Object (structural equality)Aggregate 边界速判
Aggregate boundaries?
├─ Must be consistent together in a transaction → Same aggregate
├─ Can be eventually consistent → Separate aggregates
├─ Referenced by ID only → Separate aggregates
└─ >10 entities in aggregate → Split itDomain Service or Entity method? 速判
Does it naturally belong to one entity?
├─ YES → Entity method
└─ NO ↓
Does it require multiple aggregates?
├─ YES → Domain Service
└─ NO ↓
Is it stateless business logic?
├─ YES → Domain Service
└─ NO → Reconsider placement部分端口位置变体
DDD 为中心的布局:aggregate repository 接口放在 domain/{aggregate}/repository/ 更严格 Hexagonal 布局:所有 driven ports 放在 application/ports/driven/ 两种都可以,关键是依赖仍然指向内,infrastructure 实现而非拥有抽象。
DDD Core Concepts Reference
Detailed explanation of all Domain-Driven Design strategic and tactical patterns.
Strategic Design Patterns
1. Bounded Context (限界上下文)
A bounded context is an explicit boundary within which a domain model exists. Inside the boundary, all terms and concepts have specific, well-defined meanings.
Key Characteristics:
- Each bounded context has its own ubiquitous language
- Models are consistent within the context
- Different contexts may have different models for the same business concept
Example: E-commerce System
Bounded Context: Order Management
- "Customer" means: buyer with shipping address, payment methods
- "Product" means: item with price, stock availability
Bounded Context: Customer Relations
- "Customer" means: person with contact history, support tickets
- "Product" means: item with warranty info, support documentationIdentifying Bounded Contexts: 1. Listen to how domain experts talk about the business 2. Identify when the same word means different things 3. Look for natural organizational boundaries 4. Find areas that can evolve independently
2. Ubiquitous Language (统一语言)
A language structured around the domain model and used by all team members to connect all activities of the team with the software.
Rules:
- Used in code: class names, method names, variable names
- Used in conversations: team discussions, planning meetings
- Used in documentation: specs, user stories, API docs
- Reflects the business, NOT technical implementation
Example:
BAD (Technical Language):
OrderService.processPendingOrderStatusUpdate()
GOOD (Domain Language):
Order.submit()
Order.confirmPayment()
Order.cancel(returnReason)3. Context Mapping (上下文映射)
The relationships and interactions between bounded contexts.
Relationship Patterns:
| Pattern | Description | When to Use |
|---|---|---|
| Partnership | Teams cooperate, evolve together | Close collaboration, shared goals |
| Shared Kernel | Share a subset of the model | When duplication cost > coordination cost |
| Customer-Supplier | Upstream defines, downstream consumes | Clear dependency direction |
| Conformist | Downstream conforms to upstream model | No leverage to negotiate |
| Anticorruption Layer | Translation layer between contexts | Protecting context from external model |
| Open Host Service | Protocol for any integration | Multiple consumers, standardized API |
| Published Language | Well-documented interchange format | Cross-team communication |
| Separate Ways | No integration, independent evolution | Integration cost > benefit |
4. Core Domain / Subdomain Classification
| Type | Description | Investment Level | Example |
|---|---|---|---|
| Core Domain | What makes your business unique | Maximum investment | Order pricing algorithm |
| Supporting Subdomain | Supports core, but not unique | Moderate investment | Customer notification service |
| Generic Subdomain | Common capability, could be bought | Minimal investment | Authentication, logging |
Tactical Design Patterns
1. Entity (实体)
An object defined by its identity, which remains consistent through state changes.
Rules:
- Identity is immutable and unique
- Equality is based on identity, not attributes
- Has a lifecycle (created, modified, archived/deleted)
// Entity: defined by identity
public class Order {
private OrderId id; // Identity - NEVER changes
private OrderStatus status; // Mutable state
private List<OrderLine> lines;
// Behavior, not just getters/setters
public void addItem(Product product, Quantity qty) {
// Business rule: can't add to shipped order
if (this.status == OrderStatus.SHIPPED) {
throw new OrderAlreadyShippedException(this.id);
}
// ... add logic
}
}2. Value Object (值对象)
An object defined by its attributes, with no conceptual identity.
Rules:
- Immutable — create a new instance instead of modifying
- Equality based on all attributes
- Replaceable — entirely replace one value object with another
- Self-validating — validates on construction
// Value Object: defined by attributes
@Value // Lombok: makes it immutable
public class Money {
BigDecimal amount;
Currency currency;
public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new CurrencyMismatchException();
}
return new Money(this.amount.add(other.amount), this.currency);
}
}3. Aggregate (聚合)
A cluster of domain objects treated as a single unit.
Rules:
- One aggregate = one transaction boundary
- Reference other aggregates by ID only (not direct object reference)
- The aggregate root is the only entry point
- All invariants within the aggregate must be enforced atomically
Aggregate: Order (Aggregate Root: Order entity)
├── Order (root)
│ ├── orderId: OrderId
│ ├── customerId: CustomerId ← Reference by ID
│ ├── status: OrderStatus
│ └── totalAmount: Money
├── OrderLine (entity)
│ ├── productId: ProductId ← Reference by ID
│ ├── quantity: Quantity
│ └── unitPrice: Money
└── ShippingAddress (value object)
├── street: String
├── city: String
└── zipCode: String4. Repository (仓储)
An abstraction for aggregate persistence. One repository per aggregate root.
public interface OrderRepository {
Optional<Order> findById(OrderId id);
void save(Order order);
void delete(Order order);
List<Order> findByCustomerId(CustomerId customerId);
}5. Domain Service (领域服务)
A stateless operation that doesn't naturally belong to any entity or value object.
When to use a Domain Service:
- The operation spans multiple aggregates
- The concept doesn't fit as a method on any entity
- Stateless operation
public class PricingService {
public Money calculateTotal(Order order, List<Discount> applicableDiscounts) {
// Logic that involves multiple rules, external data
// Doesn't naturally belong to Order or Discount
}
}6. Domain Event (领域事件)
Something meaningful that happened in the domain.
Naming convention: {Aggregate}{Action}Event in past tense
public class OrderPlacedEvent {
private OrderId orderId;
private CustomerId customerId;
private Money totalAmount;
private Instant occurredAt;
}
public class PaymentConfirmationFailedEvent {
private OrderId orderId;
private String failureReason;
private Instant occurredAt;
}7. Specification (规约)
An encapsulated business rule that can be combined with others.
public class ActiveOrderSpecification implements Specification<Order> {
@Override
public boolean isSatisfiedBy(Order order) {
return order.getStatus() != OrderStatus.CANCELLED
&& order.getStatus() != OrderStatus.DELIVERED;
}
}
public class HighValueOrderSpecification implements Specification<Order> {
private final Money threshold;
@Override
public boolean isSatisfiedBy(Order order) {
return order.getTotalAmount().isGreaterThan(threshold);
}
}
// Combine specifications
var spec = new ActiveOrderSpecification()
.and(new HighValueOrderSpecification(new Money("1000", USD)));The Dependency Rule (CRITICAL)
Infrastructure → Application → Domain
(adapters) (use cases) (core)
Dependencies ALWAYS point inward:
- Infrastructure depends on Application and Domain
- Application depends on Domain
- Domain depends on NOTHING externalDomain layer MUST NOT have dependencies on:
- Database frameworks (JPA, JDBC, MongoDB drivers)
- HTTP libraries (Spring MVC annotations, RestTemplate)
- Messaging (Kafka, RabbitMQ clients)
- Any infrastructure concern
Common Anti-Patterns
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Anemic Domain Model | Logic in services, entities are data bags | Move behavior into domain objects |
| Repository per Entity | Breaks aggregate boundaries | One repository per aggregate root |
| God Aggregate | Too large, performance issues | Split into smaller aggregates |
| Direct Entity References | Cross-aggregate coupling | Reference by ID only |
| CRUD Thinking | Modeling data, not behavior | Focus on business operations |
| Premature CQRS | Unnecessary complexity | Start simple, evolve when needed |
| Framework-First Design | Domain polluted by framework concerns | Domain should be framework-agnostic |
DDD Layered Architecture — 四层架构详解
层级与职责
用户接口层 (Interfaces) — DTO, Assembler, Facade
│
应用层 (Application) — 编排、事务、权限、事件发布
│
领域层 (Domain) — 核心业务逻辑、充血模型
│ ↑ 接口定义(依赖倒置)
基础层 (Infrastructure) — Repository实现, Mapper, 消息中间件, 缓存| 层 | 职责 | 关键组件 |
|---|---|---|
| 用户接口层 | 请求解析、数据展示、DTO转换 | Controller, Facade, Assembler, DTO |
| 应用层 | 服务编排、事务控制、权限校验、事件发布 | ApplicationService, EventPublisher, EventSubscriber |
| 领域层 | 核心业务逻辑、不变量校验、充血模型 | Entity, ValueObject, AggregateRoot, DomainService, Repository接口 |
| 基础层 | 技术支撑、资源访问 | Repository实现, DAO/Mapper, 消息中间件, 缓存 |
严格分层 vs 松散分层
| 模式 | 规则 | 优点 | 缺点 |
|---|---|---|---|
| 严格分层 | 每层只能依赖紧邻下层 | 服务可管理,变更影响可控 | 封装层次多 |
| 松散分层 | 任意下层可被上层调用 | 调用直接,响应快 | 变更影响难追踪 |
推荐严格分层。领域服务变更时只需逐层通知上层,不会遗漏调用方。
三层架构 → DDD 四层演进
传统三层 DDD 四层
──────── ───────
表现层 (Controller) ───→ 用户接口层(引入 DTO+Facade)
↓
业务逻辑层 (Service) ───→ 应用层(编排+事务+权限)
+ 领域层(充血模型+不变量)
↓
数据访问层 (DAO) ───→ 基础层(仓储+依赖倒置)演进关键变化
| 变化点 | 传统做法 | DDD 做法 |
|---|---|---|
| 业务逻辑 | Service 中写所有逻辑 | 实体充血 + 领域服务 + 应用编排 |
| 数据访问 | Service 直接调 DAO | Repository 接口在领域层,实现在基础层 |
| 依赖方向 | 上层依赖下层 | 领域层不依赖基础层(依赖倒置) |
| DTO 位置 | Controller 层拼数据 | 用户接口层 Assembler 转换 |
依赖倒置 (DIP)
// 领域层 — 定义接口(不依赖任何框架)
package domain.order;
public interface OrderRepository {
Order findById(OrderId id);
void save(Order order);
}
// 基础层 — 实现接口
package infrastructure.persistence;
@Repository
public class OrderRepositoryImpl implements OrderRepository {
private final OrderMapper mapper;
// 实现...
}服务编排模式
应用服务 (粗粒度)
├── 领域服务A (细粒度)
│ ├── 实体方法1
│ └── 实体方法2
├── 领域服务B
│ └── 实体方法3
└── 仓储接口 (数据持久化)服务演进
初始:领域服务 a, b, c 各自独立
↓ 多次编排后发现 b+c 总是同一组合
优化:合并为新领域服务 (b+c)
↓ 应用层编排简化
结果:领域模型越来越精炼以聚合为单位的架构演进
微服务 1:
├── 聚合 A (热点,拖累整体性能)
├── 聚合 B
└── 聚合 C
演进 ↓
微服务 1: 微服务 2 (独立):
├── 聚合 B └── 聚合 A (独立部署,独立扩容)
└── 聚合 C常见错误
| 错误 | 纠正 |
|---|---|
| 应用层写业务逻辑 | 应用层只编排,if/else 业务判断在领域层 |
| 领域层 import Spring/JPA | 领域层零框架依赖 |
| Controller 直接调 Repository | 必须经过完整调用链 |
| 仓储接口放基础层 | 接口在领域层,实现才放基础层 |