领域建模入门:什么是领域模型?

说到领域模型,我脑子里第一个蹦出来的画面,是十年前一个电商项目的白板。那时候我们团队七八个人,围着白板画了一整天,就为了搞清楚「订单」和「商品」到底怎么个关系。画到最后,项目经理说:「这不就是几张图吗?能跑代码吗?」

嗯,能跑。而且跑得稳不稳,就看这几张图画得对不对。

领域模型,说白了就是用图形化的方式,把业务世界的概念和规则翻译给技术世界看。它不是数据库表设计,也不是类图大全,而是业务人员、架构师、开发之间那个「哦,原来你说的是这个」的共同语言。

核心定义:领域模型是对业务领域中核心概念、概念属性、概念之间关系的可视化抽象。它不关心技术实现,只关心「业务是什么」。

为什么需要领域模型?

我见过太多项目,需求文档写了几十页,开发一读:「这不就是CRUD吗?」结果上线后,业务方说「不对,我们说的退款不是这个意思」。为什么会这样?因为文字有歧义,而图没有。

领域模型的价值,我总结了三句话:

  • 消除歧义——一张图胜过千言万语,业务方指着图说「这个箭头不对」,比看文字文档快十倍。
  • 发现遗漏——画关系的时候,你总会发现「咦,这个对象好像少了个属性」或者「这两个实体之间是不是还缺个关联」。
  • 指导设计——模型画清楚了,代码的包结构、数据库表设计、服务拆分,基本就定了七八成。
我的习惯:每次新项目启动,我会先拉着产品经理和核心开发,花半天时间在白板上画领域模型。画完拍张照,这就是后续所有设计的「宪法」。

UML类图:领域模型的通用语言

UML类图是画领域模型最常用的工具。你不需要记住所有UML符号,掌握下面这几个就够了:

元素 符号 含义
类(Class) 矩形框,分三格:类名、属性、方法 表示一个业务概念
关联(Association) 实线箭头 表示两个类之间有联系
多重性(Multiplicity) 1, 0..*, 1..* 等 表示关联的数量关系
聚合/组合 空心/实心菱形箭头 表示整体-部分关系

你想想看,一个电商系统里,「用户」和「订单」之间是什么关系?一个用户可以有多个订单,一个订单只属于一个用户。这就是典型的一对多关系。用UML画出来,就是两个矩形框之间连一条线,线上标上「1」和「0..*」。

实体识别:从需求中挖出核心概念

实体识别是领域建模的第一步。怎么做?我有个笨办法,但很管用:把需求文档里的名词全部圈出来。

举个例子,假设我们要做一个在线课程系统,需求里有这么一段话:

「用户可以购买课程,每个课程包含多个章节,每个章节下有多个视频。用户可以给课程写评价,一个课程可以有多个评价。」

圈出名词:用户、课程、章节、视频、评价。这些就是候选实体。

然后问三个问题:

  1. 这个名词有独立存在的意义吗?——「视频」不能脱离「章节」存在,但它本身有属性(时长、大小、播放地址),所以是实体。
  2. 这个名词有属性吗?——「用户」有用户名、邮箱、注册时间,有属性就是实体。
  3. 这个名词需要被追踪吗?——「评价」需要知道是谁写的、什么时候写的,需要追踪,所以是实体。
我曾经踩过的坑:有一次我把「地址」直接作为用户的属性,后来发现一个用户有多个收货地址,而且地址本身还有省市区、详细地址、邮编等属性。硬塞在用户表里,搞得表结构臃肿不堪。记住:如果一个「属性」本身还有属性,它就应该是一个实体。

关系建模:三种核心关系

实体找出来了,接下来就是画它们之间的线。UML类图里,关系主要分三种:

1. 一对一(1:1)

一个实体A对应且仅对应一个实体B。比如:一个用户对应一个身份证信息。这种关系在实际业务中相对少见,但一旦出现,往往意味着两个实体其实可以合并,或者有特殊的业务约束。

// 代码示例:一对一关系
public class User {
    private Long id;
    private String username;
    private IdCard idCard; // 一个用户只有一个身份证
}

public class IdCard {
    private Long id;
    private String idNumber;
    private User user; // 一个身份证只属于一个用户
}

2. 一对多(1:N)

这是最常见的业务关系。一个用户有多个订单,一个课程有多个章节,一个部门有多个员工。在UML类图中,我们在关联线的「多」端标上 0..* 或 1..*。

// 代码示例:一对多关系
public class Course {
    private Long id;
    private String title;
    private List<Chapter> chapters; // 一个课程有多个章节
}

public class Chapter {
    private Long id;
    private String name;
    private Course course; // 每个章节属于一个课程
}

3. 多对多(M:N)

一个学生可以选多门课,一门课可以有多个学生。这就是多对多。在UML类图中,关联线两端都标上 0..*。但在代码实现时,通常需要引入一个中间表或中间实体。

// 代码示例:多对多关系(通过中间实体)
public class Student {
    private Long id;
    private String name;
    private List<Enrollment> enrollments; // 一个学生有多个选课记录
}

public class Course {
    private Long id;
    private String title;
    private List<Enrollment> enrollments; // 一门课有多个选课记录
}

// 中间实体:选课记录
public class Enrollment {
    private Long id;
    private Student student;
    private Course course;
    private LocalDateTime enrollTime; // 选课时间,这是多对多关系特有的属性
}
我的建议:多对多关系一定要问业务方「这个关系有没有额外的属性?」比如选课时间、成绩、角色等。如果有,就必须拆成中间实体,而不是简单的关联表。这是很多新手容易忽略的。

实战:在线课程系统领域模型

好了,理论说完了,我们动手画一个完整的领域模型。还是那个在线课程系统,我们识别出以下实体:

  • User(用户):用户名、邮箱、角色
  • Course(课程):标题、描述、价格
  • Chapter(章节):名称、排序号
  • Video(视频):标题、时长、播放地址
  • Review(评价):评分、内容、创建时间
  • Enrollment(选课记录):选课时间、学习进度

它们之间的关系:

  • User 1 —— 0..* Enrollment(一个用户有多个选课记录)
  • Course 1 —— 0..* Enrollment(一门课有多个选课记录)
  • Course 1 —— 0..* Chapter(一门课有多个章节)
  • Chapter 1 —— 0..* Video(一个章节有多个视频)
  • User 1 —— 0..* Review(一个用户有多个评价)
  • Course 1 —— 0..* Review(一门课有多个评价)

下面我用SVG把这张图画出来:

在线课程系统领域模型(UML类图) User(用户) - id: Long - username: String - email: String Enrollment(选课记录) - enrollTime: LocalDateTime - progress: Integer Course(课程) - title: String - price: BigDecimal Chapter(章节) - name: String - sortOrder: Integer Video(视频) - title: String - duration: Integer - url: String Review(评价) - rating: Integer - content: String - createTime: LocalDateTime 1 0..* 1 0..* 1 0..* 1 0..* 1 0..* 1 0..* 图例:实线=直接关联,虚线=跨实体关联,菱形箭头=聚合关系(本章暂不展开)

建模过程中的常见误区

画了这么多年模型,我总结出几个新手最容易掉进去的坑:

  • 把数据库表设计当成领域模型——领域模型关注业务概念,不是物理存储。外键、索引这些东西不要出现在领域模型里。
  • 过度建模——一个简单的博客系统,非要画出十几个实体,连「点赞记录」都单独成一个实体。记住:模型是沟通工具,不是艺术品。
  • 忽略业务规则——比如「一个用户只能同时拥有一个进行中的课程」,这种约束在模型上怎么体现?可以在关联线上加约束说明,或者在实体里加状态字段。
  • 关系方向搞反——一对多关系里,「一」端放哪里,「多」端放哪里?我的经验是:从「主」到「从」。用户是主,订单是从,所以用户那边标1,订单那边标0..*。
我曾经犯过的错:有一次给一个金融系统建模,我把「账户」和「交易记录」画成了一对一关系。业务方看了一眼就说:「一个账户一天可能有几百笔交易,你这一对一,我的钱去哪了?」嗯,从那以后,我每次画完模型都会先找业务方过一遍,哪怕只是口头确认。

小结:领域模型的核心要点

领域模型不是什么高深莫测的东西。它就是一张图,一张让业务和技术能坐在一起聊天的图。记住三个要点:

  1. 实体从名词中来——圈出需求文档里的名词,问三个问题,实体就出来了。
  2. 关系从业务中来——1:1、1:N、M:N,每种关系都有对应的业务场景,不要凭空想象。
  3. 模型从沟通中来——画完模型,一定要找业务方确认。模型错了,后面的代码全是白写。

我个人习惯,每次画完领域模型,会打印出来贴在工位上。开发过程中遇到任何疑问,先看模型,模型上没有的,再去找业务方确认。这比翻几十页的需求文档快多了。