领域驱动设计基础:领域、子域、限界上下文、实体与值对象、领域事件

说实话,很多数据产品经理第一次接触 DDD 时,都会被这些术语吓到。我当年也一样。记得 2017 年我第一次参与一个电商中台项目,技术架构师张口就是「限界上下文」、「聚合根」,我坐在会议室里一脸懵。

后来我才明白,DDD 不是给代码加一层包装,而是帮我们理清业务到底在做什么。你想想看,如果连业务边界都画不清楚,数据产品怎么可能做对?

核心认知:领域驱动设计不是技术架构,而是一种思维方式。它强迫你先想清楚「业务是什么」,再想「系统怎么做」。

领域与子域:你的数据产品到底在解决什么问题?

领域,说白了就是你要解决的业务范围。比如你做的是电商领域,还是医疗领域,还是金融领域?每个领域都有自己独特的规则和逻辑。

但一个领域太大了,你不可能一口吃成胖子。所以我们需要拆分子域。

  • 核心子域:你的核心竞争力。比如电商的订单系统、支付系统。做不好这个,公司就活不下去。
  • 支撑子域:辅助核心业务,但也很重要。比如商品推荐、用户评价。
  • 通用子域:所有公司都差不多,直接买现成的。比如邮件通知、短信服务。

我在项目中遇到过最典型的错误,就是团队把大量精力花在通用子域上,自己写了一套邮件系统,结果核心订单模块一塌糊涂。嗯,这其实是个很常见的坑。

我的建议:做数据产品规划时,先花一周时间画出你所在领域的子域地图。把核心子域标红,支撑子域标黄,通用子域标绿。然后问自己:我的数据产品到底在服务哪个子域?

限界上下文:数据产品的边界在哪里?

限界上下文这个概念,我理解了很久才真正搞懂。说白了,它就是「一个业务概念的边界」。同一个词,在不同上下文里意思完全不一样。

举个例子,「用户」这个词:

上下文 「用户」的含义 关键属性
订单上下文 下单的人 用户ID、收货地址、支付方式
营销上下文 被触达的对象 用户ID、手机号、标签、渠道
客服上下文 需要服务的客户 用户ID、历史工单、满意度

你看,同一个「用户」,在不同上下文里关注的点完全不同。如果你强行用一个「大用户表」去覆盖所有场景,最后一定会变成谁也看不懂的怪物。

我曾经接手过一个数据平台,里面有个「客户宽表」,300 多个字段。业务方说查个东西要等 5 分钟,开发说不敢改因为不知道谁在用。这就是典型的限界上下文没划清楚。

避坑指南:我曾经见过一个团队,把订单数据和用户行为数据强行放在同一个数据模型中,结果订单字段和事件字段互相污染,查询性能一塌糊涂。记住:不同上下文的数据,就该分开建模。

实体与值对象:数据建模的基本功

这两个概念,是 DDD 里最基础但也最容易搞混的。我直接说人话:

  • 实体:有唯一标识,会变化。比如一个订单,订单号不变,但状态从「待支付」变成「已支付」。
  • 值对象:没有唯一标识,只看值。比如地址「北京市朝阳区 xxx」,换一个地址就是另一个对象。

为什么这个区分很重要?因为数据产品里,你设计的表结构、API 接口、数据模型,都依赖这个判断。

实战经验:我设计数据模型时,会先问自己三个问题:

  1. 这个对象需要被单独追踪吗?(需要 → 实体)
  2. 它的值变了,我需要记录历史吗?(需要 → 实体)
  3. 它只是描述另一个对象的属性吗?(是 → 值对象)

举个具体的例子。你在设计一个用户画像系统:

// 实体:用户
class User {
    userId: string;      // 唯一标识
    name: string;
    level: string;       // 会变化
    lastLoginTime: Date;
}

// 值对象:地址
class Address {
    province: string;
    city: string;
    district: string;
    detail: string;
    // 没有 ID,两个 Address 只要值一样就相等
}

你看,用户是实体,因为我们需要追踪他的等级变化。地址是值对象,换地址了直接替换就行,不需要知道「旧地址去哪了」。

领域事件:数据产品里的「发生了什么」

领域事件,就是业务中发生的、值得记录的事情。比如「订单已支付」、「用户已注册」、「商品已上架」。

为什么数据产品要关注领域事件?因为数据产品本质上就是在消费这些事件。你做的实时大屏、用户行为分析、异常监控,底层都是领域事件在驱动。

我建议你在设计数据产品时,先梳理出核心的领域事件清单:

事件名称 触发时机 关键数据 消费方
OrderPlaced 用户提交订单 订单ID、金额、商品列表 实时大屏、推荐系统
PaymentCompleted 支付成功 订单ID、支付方式、金额 财务系统、用户积分
UserRegistered 新用户注册 用户ID、注册渠道、时间 用户画像、营销系统

我的习惯:每个领域事件都包含三个要素:事件名称(过去时)、事件发生时间、事件携带的数据。不要往事件里塞太多东西,够用就行。我曾经见过一个事件带了 50 个字段,最后没人知道哪些是真正有用的。

一张图看懂 DDD 核心概念

下面这张图,是我自己总结的 DDD 核心概念关系图。每次给团队培训我都会画一遍,你感受一下:

DDD 核心概念关系图 领域(Domain) 核心子域 订单、支付 支撑子域 推荐、评价 通用子域 邮件、短信 限界上下文(Bounded Context) 每个子域都有自己的限界上下文,数据模型独立 实体(Entity) 有唯一标识,会变化 值对象(Value Object) 无标识,只看值 领域事件:记录「发生了什么」

从这张图你可以看到:领域包含多个子域,每个子域有自己的限界上下文,上下文里定义实体和值对象,而领域事件则贯穿整个流程,记录业务的变化。

总结一下

DDD 的基础概念,说白了就是帮你回答几个问题:

  • 你的业务范围是什么?(领域)
  • 核心在哪,辅助在哪,通用的在哪?(子域)
  • 每个模块的边界在哪,数据模型怎么独立?(限界上下文)
  • 哪些东西需要追踪变化,哪些只是描述?(实体 vs 值对象)
  • 业务中发生了什么值得记录的事?(领域事件)