<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>边城码志</title><description>边城仔的个人博客，以码为志，持续深耕</description><link>https://www.bczai.xyz/</link><language>zh_CN</language><item><title>DDD 落地实践：分层架构与建模示例</title><link>https://www.bczai.xyz/posts/ddd-in-practice/</link><guid isPermaLink="true">https://www.bczai.xyz/posts/ddd-in-practice/</guid><description>用「图书借阅」这个贴近日常的业务例子，把战略设计和战术设计的概念完整串起来，演示一个典型的 DDD 分层架构与建模过程。</description><pubDate>Wed, 26 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;前几篇讲完了概念，这篇用一个真实的例子——&lt;strong&gt;图书借阅&lt;/strong&gt;，把 DDD 从战略设计到战术设计、再到代码分层完整串一遍。理解了这一个例子，你就掌握了 DDD 落地的主线。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;例子的背景&lt;/h2&gt;
&lt;p&gt;假设我们要做一个&lt;strong&gt;图书馆借阅系统&lt;/strong&gt;。为了简化，它涉及这样几个业务对象：读者、图书、借阅记录。&lt;/p&gt;
&lt;p&gt;如果沿用传统的三层架构，很可能会写成这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Controller → Service → DAO（直接用数据库表）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;数据库里可能直接有 &lt;code&gt;book&lt;/code&gt; 表（含库存）、&lt;code&gt;reader&lt;/code&gt; 表、&lt;code&gt;borrow_record&lt;/code&gt; 表。业务逻辑（比如&quot;这本书还能不能借&quot;）被散落在 Service 的多个方法里，或者干脆写在 SQL 里。等业务复杂了，就难以维护。&lt;/p&gt;
&lt;p&gt;下面我们看看用 DDD 怎么重新组织。&lt;/p&gt;
&lt;h2&gt;第一步：战略设计——划定边界&lt;/h2&gt;
&lt;p&gt;对于这个小系统，我们可以先做战略设计的思考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心子域&lt;/strong&gt;：借阅管理。这是系统最核心的价值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;支撑子域&lt;/strong&gt;：图书管理（藏书、库存），支撑借阅。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;通用子域&lt;/strong&gt;：读者账号、权限，用现成方案即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后划出&lt;strong&gt;限界上下文&lt;/strong&gt;，至少分成两个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;图书上下文&lt;/strong&gt;：管理图书信息、库存、上架下架。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;借阅上下文&lt;/strong&gt;：管理借阅流程、逾期、归还。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;你会发现，&quot;图书&quot;在两个上下文里的含义不同：图书上下文里图书有&quot;库存&quot;，借阅上下文里图书只是&quot;可以被借的东西&quot;。这正是限界上下文存在的意义。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;第二步：战术设计——借阅上下文内部建模&lt;/h2&gt;
&lt;p&gt;聚焦&lt;strong&gt;借阅上下文&lt;/strong&gt;，我们来找实体、值对象和聚合。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实体（有身份、会变化）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Borrower&lt;/code&gt;（借阅者）—— 有 ID。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Borrow&lt;/code&gt;（借阅记录）—— 有 ID，状态会从&quot;借出&quot;变到&quot;归还&quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;值对象（无身份、不可变）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BookId&lt;/code&gt;（图书 ID）—— 只是对一本具体书的引用。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DueDate&lt;/code&gt;（应还日期）—— 一个日期值，没有身份。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BorrowPeriod&lt;/code&gt;（借阅周期）—— 包含开始与应还日期。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;聚合 + 聚合根&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;借阅记录 &lt;code&gt;Borrow&lt;/code&gt; 可以作为一个&lt;strong&gt;聚合根&lt;/strong&gt;，它包含 &lt;code&gt;Borrower&lt;/code&gt; 和几条 &lt;code&gt;BorrowItem&lt;/code&gt;（借了哪几本书），对外只允许通过 &lt;code&gt;Borrow&lt;/code&gt; 来操作。这样&quot;一次借阅&quot;这个业务动作的一致性就有保证了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;领域服务&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&quot;借书&quot;这个动作会同时判断图书库存、更新借阅记录，跨越了图书上下文和借阅上下文，所以可以放到&lt;strong&gt;领域服务&lt;/strong&gt; &lt;code&gt;BorrowingService&lt;/code&gt; 里。&lt;/p&gt;
&lt;h2&gt;第三步：分层架构&lt;/h2&gt;
&lt;p&gt;DDD 常用的分层是 &lt;strong&gt;四层架构&lt;/strong&gt;，从外到内：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;展现层（Controller / 前端 API）
      ↓
应用层（Application Service：编排用例、事务）
      ↓
领域层（Domain：实体、值对象、聚合、领域服务、仓储接口）★核心
      ↓
基础设施层（Infrastructure：仓储实现、数据库、外部调用）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键在&lt;strong&gt;依赖方向&lt;/strong&gt;：依赖永远指向&lt;strong&gt;领域层&lt;/strong&gt;。也就是说：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;领域层&lt;strong&gt;不依赖&lt;/strong&gt;任何技术细节（不 import 数据库、不依赖框架）；&lt;/li&gt;
&lt;li&gt;应用层依赖领域层的接口；&lt;/li&gt;
&lt;li&gt;基础设施层实现领域层定义的仓储接口。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样，领域层（也就是业务本身）就变得纯粹、可测试、不受技术绑架。&lt;/p&gt;
&lt;h2&gt;第四步：看看代码大致长什么样&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;领域层 —— 值对象 &lt;code&gt;BookId&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public record BookId(String value) {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;领域层 —— 聚合根 &lt;code&gt;Borrow&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Borrow {
    private final BorrowId id;
    private final BorrowerId borrowerId;
    private final List&amp;lt;BorrowItem&amp;gt; items;
    private BorrowStatus status;

    // 只有聚合根能改变自己的状态
    public void returnBooks() {
        if (this.status == BorrowStatus.RETURNED) {
            throw new IllegalStateException(&quot;already returned&quot;);
        }
        this.status = BorrowStatus.RETURNED;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;领域层 —— 仓储接口&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface BorrowRepository {
    Borrow findById(BorrowId id);
    void save(Borrow borrow);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;基础设施层 —— 仓储实现&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class JpaBorrowRepository implements BorrowRepository {
    @Override
    public Borrow findById(BorrowId id) {
        // 用 JPA / MyBatis 等具体技术实现
        return ...;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到，&lt;strong&gt;领域层代码里没有任何数据库或框架的影子&lt;/strong&gt;，它只表达业务。这正是 DDD 想达到的效果。&lt;/p&gt;
&lt;h2&gt;落地时的常见误区&lt;/h2&gt;
&lt;p&gt;最后，整理几个我踩过/看到过的坑：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;不要为了 DDD 而 DDD&lt;/strong&gt;：业务简单时强行分层只会增加复杂度。DDD 的价值在复杂业务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要把领域层写脏&lt;/strong&gt;：最常见的失败是领域层里出现了 &lt;code&gt;@Entity&lt;/code&gt;、&lt;code&gt;Repository&lt;/code&gt; 的实现、甚至 SQL，导致领域层被技术绑架。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;应用层要&quot;薄&quot;&lt;/strong&gt;：应用层只做编排和事务，真正的业务规则要下沉到领域层。应用层胖了，就是&quot;贫血模型&quot;回潮。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;从小处开始&lt;/strong&gt;：先在一个上下文试点，别指望一步到位。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;这一系列四篇笔记，完整走了一遍 DDD：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;认识 DDD&lt;/strong&gt;：以业务领域为核心的方法论。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;战略设计&lt;/strong&gt;：子域划分、限界上下文、统一语言。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;战术设计&lt;/strong&gt;：实体、值对象、聚合、仓储、领域服务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;落地实践&lt;/strong&gt;：分层架构 + 一个完整建模示例。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你和我一样，正在用 DDD 的项目里摸索，建议从&lt;strong&gt;战略设计的限界上下文&lt;/strong&gt;和&lt;strong&gt;战术设计的聚合&lt;/strong&gt;这两个概念抓起——它们是最能体现 DDD 价值、也最容易被忽视的地方。&lt;/p&gt;
&lt;p&gt;祝我们都能在 DDD 的路上少踩点坑。😄&lt;/p&gt;
</content:encoded></item><item><title>DDD 战术设计：实体、值对象与聚合</title><link>https://www.bczai.xyz/posts/ddd-tactical-design/</link><guid isPermaLink="true">https://www.bczai.xyz/posts/ddd-tactical-design/</guid><description>战略设计划定了边界，战术设计决定边界内部怎么建模。这篇笔记用通俗的比喻讲清楚实体、值对象、聚合、仓储、领域服务这几个战术概念。</description><pubDate>Mon, 24 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;战略设计决定了系统的骨架，战术设计则决定了骨架内部的肌肉。这篇笔记用尽量通俗的方式，讲清楚战术设计中几个最核心的概念。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;实体（Entity）：有身份的&quot;东西&quot;&lt;/h2&gt;
&lt;p&gt;实体是&lt;strong&gt;有唯一标识、且会变化&lt;/strong&gt;的对象。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;类比：&lt;strong&gt;一个人&lt;/strong&gt;。哪怕他改了名、换了工作、搬了家，他还是&quot;这个人&quot;——因为他的身份证号不变。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对应到代码，实体的关键是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有&lt;strong&gt;唯一标识&lt;/strong&gt;（ID）；&lt;/li&gt;
&lt;li&gt;有&lt;strong&gt;生命周期&lt;/strong&gt;，状态会随业务变化；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;身份稳定&lt;/strong&gt;，ID 是判断&quot;是不是同一个&quot;的标准。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;比如订单、用户、账户，都是典型的实体。两个订单即使内容完全一样，也是两个不同的对象，因为它们的 ID 不同。&lt;/p&gt;
&lt;h2&gt;值对象（Value Object）：只描述&quot;值&quot;的东西&lt;/h2&gt;
&lt;p&gt;值对象是&lt;strong&gt;没有独立身份、完全由属性值决定&lt;/strong&gt;的对象。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;类比：&lt;strong&gt;一个地址&lt;/strong&gt;。北京市朝阳区某某路 1 号——这就是它本身，你不需要给这个地址一个&quot;ID&quot;。换了个地址，就是换了个新对象。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;值对象的特征：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;没有身份&lt;/strong&gt;，两个内容相同的值对象就是&quot;相等&quot;的；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不可变&lt;/strong&gt;，值对象创建后不应被修改；&lt;/li&gt;
&lt;li&gt;常用来封装一些概念，如地址、金额、坐标、时间段。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;比如&quot;金额&quot;可以做成值对象，包含 &lt;code&gt;金额数值&lt;/code&gt; 和 &lt;code&gt;币种&lt;/code&gt;，而不是裸用一个 &lt;code&gt;double&lt;/code&gt;。这样能避免&quot;不同币种乱相加&quot;这类低级错误。&lt;/p&gt;
&lt;h3&gt;实体 vs 值对象&lt;/h3&gt;
&lt;p&gt;判断一个对象该做成实体还是值对象，就看它&lt;strong&gt;有没有独立身份&lt;/strong&gt;、&lt;strong&gt;是否可共享复用&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;判断维度&lt;/th&gt;
&lt;th&gt;实体&lt;/th&gt;
&lt;th&gt;值对象&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;是否有 ID&lt;/td&gt;
&lt;td&gt;有&lt;/td&gt;
&lt;td&gt;没有&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;是否可变&lt;/td&gt;
&lt;td&gt;会变&lt;/td&gt;
&lt;td&gt;不可变&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;是否被共享&lt;/td&gt;
&lt;td&gt;独立拥有&lt;/td&gt;
&lt;td&gt;可被共享引用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;例子&lt;/td&gt;
&lt;td&gt;订单、用户&lt;/td&gt;
&lt;td&gt;金额、地址&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;聚合（Aggregate）：一组对象的边界&lt;/h2&gt;
&lt;p&gt;聚合是 DDD 战术设计里&lt;strong&gt;最重要的概念&lt;/strong&gt;。它把一组关联紧密的对象绑定在一起，由一个&lt;strong&gt;聚合根&lt;/strong&gt;统一对外提供访问。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;类比：&lt;strong&gt;一辆汽车&lt;/strong&gt;。发动机、变速箱、轮子是它的部件，但你要启动汽车，只需要操作&quot;启动按钮&quot;（聚合根），不需要直接去操作每一个零件。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;聚合的三个要点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;一个聚合根&lt;/strong&gt;：对外唯一的入口，只有通过聚合根才能修改聚合内部的对象；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;一致性边界&lt;/strong&gt;：聚合内部的数据变更必须保持业务上的一致性；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据访问最小化&lt;/strong&gt;：外部不能绕过聚合根直接操作内部对象。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;为什么需要聚合？&lt;/h3&gt;
&lt;p&gt;聚合是为了解决&lt;strong&gt;事务一致性和并发控制&lt;/strong&gt;的复杂度。如果不设聚合，所有对象之间都能随意互相引用、互相修改，数据一致性就完全失控了。&lt;/p&gt;
&lt;p&gt;聚合有一个重要设计原则：&lt;strong&gt;优先用小聚合&lt;/strong&gt;。聚合越小，事务范围越小，并发冲突越少，系统扩展性越好。不要贪大。&lt;/p&gt;
&lt;h2&gt;仓储（Repository）：数据访问的抽象&lt;/h2&gt;
&lt;p&gt;仓储负责&lt;strong&gt;在领域模型和持久化存储之间做桥接&lt;/strong&gt;。它让领域层不需要关心数据到底存的是数据库、还是 Redis、还是文件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;类比：仓储就像&lt;strong&gt;书架&lt;/strong&gt;。书架帮你把书放好、按需取回，但你不用关心书是放在书架的哪一格。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;仓储的要点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;面向聚合根设计，一个聚合对应一个仓储；&lt;/li&gt;
&lt;li&gt;只暴露领域需要的接口（如 &lt;code&gt;findById&lt;/code&gt;、&lt;code&gt;save&lt;/code&gt;），把数据库细节藏在实现里；&lt;/li&gt;
&lt;li&gt;让上层可以方便地替换持久化方案。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;领域服务（Domain Service）：放不下实体里的业务逻辑&lt;/h2&gt;
&lt;p&gt;有些业务逻辑&lt;strong&gt;不属于某一个实体&lt;/strong&gt;，而是跨多个实体/聚合协作。这时就用&lt;strong&gt;领域服务&lt;/strong&gt;来承载。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;类比：&lt;strong&gt;一次转账&lt;/strong&gt;。转账涉及转出账户和转入账户两个对象，它既不属于转出账户，也不属于转入账户，于是把它放在一个&quot;转账服务&quot;里。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;判断是不是领域服务的标准：&lt;strong&gt;这个操作是否不适合放在任何一个实体/值对象里&lt;/strong&gt;。如果可以放，就不要硬造服务，否则会退化成&quot;贫血模型&quot;。&lt;/p&gt;
&lt;h2&gt;战术设计的小总结&lt;/h2&gt;
&lt;p&gt;战术设计提供了一套建模工具箱：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;概念&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;实体&lt;/td&gt;
&lt;td&gt;有身份的、会变化的核心对象&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;值对象&lt;/td&gt;
&lt;td&gt;无身份、不可变、按值相等的对象&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;聚合&lt;/td&gt;
&lt;td&gt;由聚合根统领的一致性边界&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;仓储&lt;/td&gt;
&lt;td&gt;聚合与持久化的桥接&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;领域服务&lt;/td&gt;
&lt;td&gt;承载跨对象的业务逻辑&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这几个概念组合起来，就能把某个限界上下文内部建模得清晰、内聚。下一篇笔记，我会用一个实际的业务例子，把这些概念完整串起来，看看 DDD 到底怎么落地。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/posts/ddd-in-practice/&quot;&gt;下一篇：DDD 落地实践：分层架构与建模示例&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>DDD 战略设计：领域、子域与限界上下文</title><link>https://www.bczai.xyz/posts/ddd-strategic-design/</link><guid isPermaLink="true">https://www.bczai.xyz/posts/ddd-strategic-design/</guid><description>战略设计是 DDD 最重要也最容易被忽略的部分。这篇笔记详细解释领域、核心子域、支撑子域、通用子域以及限界上下文这几个核心概念。</description><pubDate>Sat, 22 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;上一篇笔记讲了 DDD 的整体认识，这篇深入到 DDD 的第一个层次——&lt;strong&gt;战略设计&lt;/strong&gt;。它是决定整个系统边界的关键一步。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;什么是领域（Domain）&lt;/h2&gt;
&lt;p&gt;&quot;领域&quot;就是&lt;strong&gt;软件要解决的问题所在的业务范围&lt;/strong&gt;。比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个电商系统，领域是&quot;在线交易&quot;；&lt;/li&gt;
&lt;li&gt;一个医院挂号系统，领域是&quot;医疗服务&quot;；&lt;/li&gt;
&lt;li&gt;一个物流系统，领域是&quot;货物运输&quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;领域并不是一个抽象的大词，它是由&lt;strong&gt;一系列业务活动、规则、概念&lt;/strong&gt;组成的。开发这个系统的人，本质上是在用代码重新描述这个领域。&lt;/p&gt;
&lt;h2&gt;子域（Subdomain）：把领域拆小&lt;/h2&gt;
&lt;p&gt;一个真实的业务领域往往非常大。比如&quot;电商&quot;这个领域，包含商品、库存、订单、支付、营销、物流、会员…… 直接对整个领域建模会非常困难。所以战略设计的第一步，就是把大领域拆分成若干&lt;strong&gt;子域（Subdomain）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;子域一般分成三类：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;特点&lt;/th&gt;
&lt;th&gt;例子（电商）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;核心子域&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;公司最有竞争力、最不能外包的部分&lt;/td&gt;
&lt;td&gt;订单、交易&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;支撑子域&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;重要但非核心，可自研也可外包&lt;/td&gt;
&lt;td&gt;库存、营销&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;通用子域&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;没有业务特性，行业通用，通常用现成方案&lt;/td&gt;
&lt;td&gt;权限、日志、认证&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;拆分时最重要的一点是：&lt;strong&gt;资源要优先投给核心子域&lt;/strong&gt;。核心子域是最值得投入精力的地方，通用子域直接用成熟方案（比如用现成的鉴权框架），没必要自己造轮子。&lt;/p&gt;
&lt;h2&gt;限界上下文（Bounded Context）：真正的边界&lt;/h2&gt;
&lt;p&gt;如果说&quot;子域&quot;是&lt;strong&gt;业务视角&lt;/strong&gt;的划分，那么&quot;限界上下文&quot;就是&lt;strong&gt;技术/实现视角&lt;/strong&gt;的边界划分。&lt;/p&gt;
&lt;p&gt;限界上下文是 DDD 里我认为最核心的概念。它的含义是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;一个限界上下文就是一个明确的边界，在这个边界内，模型、术语、规则是一致的。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;回到之前&quot;订单状态&quot;的例子：电商团队的 &lt;code&gt;订单&lt;/code&gt; 和物流团队的 &lt;code&gt;订单&lt;/code&gt;，其实是两个不同限界上下文里的不同模型。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在&lt;strong&gt;交易上下文&lt;/strong&gt;里，订单关注&quot;是否支付、是否超时、是否关闭&quot;；&lt;/li&gt;
&lt;li&gt;在&lt;strong&gt;履约上下文&lt;/strong&gt;里，订单关注&quot;是否拣货、是否出库、是否签收&quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它们都叫&quot;订单&quot;，但关心的事实和规则完全不同。如果硬要用一个&quot;大而全&quot;的订单模型去覆盖所有场景，必然导致混乱。&lt;/p&gt;
&lt;h3&gt;为什么需要限界上下文？&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;核心原因：一个统一的模型无法满足所有场景。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不同场景对同一概念的需求是矛盾的。比如&quot;商品&quot;在&lt;strong&gt;销售上下文&lt;/strong&gt;里需要&quot;价格、促销&quot;属性，在&lt;strong&gt;仓储上下文&lt;/strong&gt;里需要&quot;库存量、存放货架&quot;属性。强行统一会让模型臃肿，也让每个团队都要迁就别人。&lt;/p&gt;
&lt;p&gt;所以限界上下文的做法是：&lt;strong&gt;各自维护自己的模型，通过明确的上下文边界隔离复杂度。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;上下文之间的集成&lt;/h3&gt;
&lt;p&gt;既然拆成了多个上下文，它们之间就需要协作。DDD 提供了一些**上下文映射模式（Context Mapping）**来定义协作关系，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;防腐层（Anti-Corruption Layer）&lt;/strong&gt;：隔离外部系统对自身模型的污染。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共享内核（Shared Kernel）&lt;/strong&gt;：两个上下文共享一小部分稳定模型。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;开放主机服务（Open Host Service）&lt;/strong&gt;：通过公开 API 对外提供服务。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些模式里，日常用得最多、也最实用的就是&lt;strong&gt;防腐层&lt;/strong&gt;——当你的系统要对接一个模型很混乱的旧系统或第三方系统时，防腐层能把外部复杂性挡在边界之外。&lt;/p&gt;
&lt;h2&gt;统一语言（Ubiquitous Language）&lt;/h2&gt;
&lt;p&gt;战略设计里还有一个贯穿始终的概念——&lt;strong&gt;统一语言&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它要求业务人员和开发人员在同一个限界上下文内，&lt;strong&gt;使用完全一致的术语&lt;/strong&gt;。业务说&quot;下单&quot;、&quot;退款&quot;、&quot;驳回&quot;，代码里就应该是 &lt;code&gt;placeOrder&lt;/code&gt;、&lt;code&gt;refund&lt;/code&gt;、&lt;code&gt;reject&lt;/code&gt;，而不是 &lt;code&gt;createOrder&lt;/code&gt;、&lt;code&gt;payRollback&lt;/code&gt;、&lt;code&gt;updateStatusToXxx&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;统一语言的价值在于：当业务人员能读懂你的领域模型，代码和业务就真正对齐了。这也要求我们&lt;strong&gt;模型命名要贴近业务语言&lt;/strong&gt;，而不是贴近数据库表名。&lt;/p&gt;
&lt;h2&gt;小结&lt;/h2&gt;
&lt;p&gt;战略设计回答了&quot;系统边界在哪&quot;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用&lt;strong&gt;子域&lt;/strong&gt;从业务视角拆分大领域，并识别出&lt;strong&gt;核心子域&lt;/strong&gt;优先投入。&lt;/li&gt;
&lt;li&gt;用&lt;strong&gt;限界上下文&lt;/strong&gt;从实现视角划定模型边界，隔离复杂度。&lt;/li&gt;
&lt;li&gt;用&lt;strong&gt;统一语言&lt;/strong&gt;保证业务与代码术语一致。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下一步，进入 DDD 的第二个层次——&lt;strong&gt;战术设计&lt;/strong&gt;，看看在某个上下文内部，实体、值对象、聚合这些战术概念怎么用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/posts/ddd-tactical-design/&quot;&gt;下一篇：DDD 战术设计：实体、值对象与聚合&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>什么是领域驱动设计（DDD）？</title><link>https://www.bczai.xyz/posts/ddd-introduction/</link><guid isPermaLink="true">https://www.bczai.xyz/posts/ddd-introduction/</guid><description>从零认识领域驱动设计：它是什么、解决什么问题、为什么技术团队越来越推崇它。适合刚开始接触 DDD 的读者。</description><pubDate>Thu, 20 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;我最近在用一个采用 DDD 思想的项目，但一直没完全理解它。这篇是我的学习笔记，希望能帮助同样刚接触 DDD 的朋友建立起整体的认识。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;为什么会出现 DDD？&lt;/h2&gt;
&lt;p&gt;传统的分层架构（Controller → Service → DAO）在业务简单的系统里非常高效。但随着业务变得越来越复杂，很多团队发现一个普遍的问题：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;技术代码写得越来越熟练，但业务逻辑却越来越混乱。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;举个典型的例子：一个&quot;订单&quot;状态。在代码里，订单可能有 &lt;code&gt;status&lt;/code&gt; 字段，电商团队写 &lt;code&gt;status = 2&lt;/code&gt; 表示&quot;已支付&quot;，物流团队写 &lt;code&gt;status = &quot;paid&quot;&lt;/code&gt;，报表团队又有一套 &lt;code&gt;orderState&lt;/code&gt;。同一个业务概念，在不同系统里有着不同的表达。业务方说&quot;改一下订单状态&quot;，你根本不知道要改哪一行代码。&lt;/p&gt;
&lt;p&gt;DDD（Domain-Driven Design，领域驱动设计）就是针对这类问题提出的方法论。它的核心思想是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;软件系统的复杂性和核心价值都在于业务领域本身，而不是技术实现。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因此，软件开发应该以&quot;领域&quot;为核心，让&lt;strong&gt;技术为业务服务&lt;/strong&gt;，而不是让业务去迁就技术。&lt;/p&gt;
&lt;h2&gt;DDD 关注什么&lt;/h2&gt;
&lt;p&gt;DDD 大致可以分成两个层次：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;层次&lt;/th&gt;
&lt;th&gt;关注点&lt;/th&gt;
&lt;th&gt;回答的问题&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;战略设计&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;系统边界、上下文划分&lt;/td&gt;
&lt;td&gt;整个系统怎么拆？哪些是核心？&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;战术设计&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;代码内部的建模&lt;/td&gt;
&lt;td&gt;一个模块内部的类怎么设计？&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;战略设计回答&quot;&lt;strong&gt;拆成几块、边界在哪&lt;/strong&gt;&quot;，战术设计回答&quot;&lt;strong&gt;每一块里面长什么样&lt;/strong&gt;&quot;。两者是递进关系：先定边界，再细化内部。&lt;/p&gt;
&lt;h2&gt;几个最常见的误解&lt;/h2&gt;
&lt;p&gt;接触 DDD 时，很容易陷入下面几个误区：&lt;/p&gt;
&lt;h3&gt;误解一：DDD 就是&quot;领域模型 + 贫血/充血模型&quot;&lt;/h3&gt;
&lt;p&gt;很多人一听说 DDD 就以为是&quot;把业务逻辑塞进实体类里&quot;。其实这只是&lt;strong&gt;战术设计&lt;/strong&gt;里的一个细节。DDD 最重要的贡献是&lt;strong&gt;战略设计&lt;/strong&gt;——教会大家如何划分业务边界，而这部分经常被忽略。&lt;/p&gt;
&lt;h3&gt;误解二：DDD 只适合大型项目&lt;/h3&gt;
&lt;p&gt;DDD 确实在复杂系统里收益最大，但这不意味着小项目不能用。关键在于&lt;strong&gt;建模思维&lt;/strong&gt;：先把业务领域想清楚，再写代码。这个习惯对任何规模的项目都有价值。&lt;/p&gt;
&lt;h3&gt;误解三：DDD 是一种框架&lt;/h3&gt;
&lt;p&gt;DDD 不是框架，也不是某个库。它是一种&lt;strong&gt;思考和建模的方法论&lt;/strong&gt;。你完全可以在 Spring Boot、Go、Node.js 里落地 DDD，也可以用手写代码的方式落地。&lt;/p&gt;
&lt;h2&gt;DDD 的核心价值&lt;/h2&gt;
&lt;p&gt;我觉得 DDD 最大的价值有三个：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;统一语言&lt;/strong&gt;：让业务人员和开发人员使用同一套术语交流，避免&quot;鸡同鸭讲&quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;清晰的边界&lt;/strong&gt;：通过限界上下文划分系统边界，控制复杂度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;业务为核心&lt;/strong&gt;：让软件模型贴近真实业务，业务变了代码知道怎么改。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;小结&lt;/h2&gt;
&lt;p&gt;这篇文章只是 DDD 的&quot;地图&quot;，先建立整体印象。接下来的几篇笔记我会分别展开：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/posts/ddd-strategic-design/&quot;&gt;战略设计：领域、子域与限界上下文&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/posts/ddd-tactical-design/&quot;&gt;战术设计：实体、值对象与聚合&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/posts/ddd-in-practice/&quot;&gt;落地实践：分层架构与建模示例&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;先从最重要的&lt;strong&gt;战略设计&lt;/strong&gt;开始看吧，它决定了整个系统的骨架。&lt;/p&gt;
</content:encoded></item></channel></rss>