领域模型——正向从业务建模型,逆向从模型展开成流程

领域模型有两条方向:正向是从业务语言/现实问题分析出对象、属性、关系、规则与状态(把业务世界压缩成一个模型);逆向是把已经建好的模型展开成开发流程——转成具体的步骤序列,每一步做什么、产出什么都有明确约定。正向回答「业务世界里有什么」,逆向回答「有了模型之后按什么顺序、一步步把它变成程序」。


一、正向:从业务语言到领域模型

1.1 一个例子:图片管理器

要做一个图片管理器,不能直接开始写界面。先问:这个软件操作的对象是什么?

1
2
3
4
5
6
Project          项目(一组图片的集合)
Folder 文件夹(图片分组)
ImageFile 图片文件
ImageMetadata 图片元数据(尺寸/格式/拍摄时间)
SearchQuery 搜索条件
EditorDocument 编辑中的文档

这就是领域模型的第一步:识别领域对象

再往下,对象有属性、有关系、有状态:

1
2
3
4
5
6
7
8
9
Project
├── 属性:name, path, cover
├── 关系:包含多个 Folder
└── 状态:NoProject → Loading → Opened → NoProject

ImageFile
├── 属性:width, height, format, size
├── 关系:属于 Folder
└── 状态:Closed → Opened → Modified → Saving → Saved

界面、仓库、库本质上都是领域对象的可视化/持久化操作接口。 界面上的每个按钮、列表、对话框,背后都对应一个领域对象或对它的操作。

1.2 正向怎么建:从现实问题分析抽象

建领域模型的完整路径:

1
2
3
4
5
6
7
8
9
10
11
现实问题(业务世界)/ 业务语言
↓ 分析:这个业务里有什么东西?
领域对象(名词)
↓ 分析:每个对象有什么属性?
属性(数据)
↓ 分析:对象之间什么关系?
关系(包含/属于/依赖/触发)
↓ 分析:有什么规则和状态变化?
规则 + 状态

领域模型

① 找对象(名词)

业务描述里的名词往往是领域对象:

1
2
3
「用户可以新建项目,把图片按文件夹整理,搜索时按元数据过滤」
↓ 名词
用户 / 项目 / 图片 / 文件夹 / 搜索 / 元数据

从这里挑出软件要操作的、有状态的对象:Project / Folder / ImageFile / SearchQuery。

② 找属性

每个对象回答「它是什么」:

1
ImageFile:路径、格式、宽度、高度、大小、修改时间

③ 找关系

对象之间回答「谁和谁怎么连接」:

1
2
3
Project ─包含→ Folder ─包含→ ImageFile
ImageFile ─拥有→ ImageMetadata
SearchQuery ─筛选→ ImageFile

④ 找规则和状态

回答「对象怎么变化、哪些变化是允许的」:

1
2
ImageFile:Closed → Opened → Modified → Saving → Saved
Project: NoProject → Loading → Opened → NoProject(保存中禁止编辑)

四步做完,领域模型就成型了——它描述的是业务世界的结构,与界面、数据库、语言都无关。 这一步本身就是「正向建模」在领域上的应用成果:从具体业务里抽出共同结构与规则,形成一个模型(见 03-建模与逆向 正向建模)。


二、逆向:从领域模型展开成流程与步骤

模型只是空间结构(谁包含谁、谁依赖谁),工程却是时间顺序(先做什么、后做什么)。领域模型的逆向不是「重新拆模型」,而是把模型展开成一条可执行的流程——一个包含每个步骤做什么的序列。

1
2
3
4
5
6
7
领域模型(空间:对象 / 属性 / 关系 / 规则 / 状态)
↓ 逆向
流程(时间:步骤 1 → 步骤 2 → 步骤 3 → …)

每个步骤:明确做什么、产出什么

可以执行:照着流程一步步做出程序

2.1 逆向的核心:先按依赖排序,再逐步展开

把模型转成步骤,依据的是对象之间的依赖

  • 没有依赖的对象先做(基础数据先成型);
  • 被依赖的结尾后做(依赖它们的逻辑最后做)。

以图片管理器为例,领域模型对象存在依赖:

1
2
ImageMetadata → ImageFile → Folder → Project → SearchQuery → 状态模型
(基础数据) (组合数据) (容器) (项目根) (检索) (行为)

于是得到步骤序列(每个步骤都产出可验证的东西):

步骤 做什么 产出(可检查)
1 定义基础数据对象 ImageMetadata(尺寸/格式/时间)
2 定义核心数据对象 ImageFile(元数据 + 内容路径)
3 定义容器对象 Folder(图片列表)/ Project(文件夹 + 元数据)
4 定义查询对象 SearchQuery(条件/过滤规则)
5 定义状态模型 Project / ImageFile 的状态转换(见下)
6 定义对象操作 打开/保存/删除/搜索(对模型中的对象的操作)
7 连上界面 界面上的动作 = 领域对象的操作(右键删除 = project.delete(image))

每一步都「做什么」定了,产出也可检查,这就是从模型展开出的第一条流程。

2.2 状态模型:领域模型中规则与状态的落地

模型里的规则(状态)单独展开成 状态模型(State Model)——它就是前一步中「状态转换表」的展示:

1
2
3
4
5
6
7
8
ImageFile 的状态模型:
Closed ─loadFile()→ Loading ─load成功→ Opened ─save()→ Saving ─save完成→ Saved
│ │ │
load失败 │ save失败
↓ ↓ ↓
Error ←──────────────┴─────────────────────────┘
规则:Loading 中不允许 save();
Error 只出现在 load 失败后

界面上的按钮可用/不可用,就是状态模型的投影Closed 时「保存」按钮 disabled;Opened|Saving 时才可用。

2.3 从模型逆向出完整流程(GUI 图片查看器案例)

从领域模型出发逆向出完整工程流程——每一步都清楚「做什么」:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
领域模型(Image/ImageCollection/ImageMetadata/ViewState)

① 用户流程:启动 → 选择图片 → 加载 → 显示 → 操作(缩放/旋转/上下张) → 退出

② 界面信息架构:MainWindow → MenuBar / ToolBar / ImageView / StatusBar

③ 交互设计:点击 Open → 文件对话框 → 选择 → 加载 → 显示;异常→错误提示

④ 状态设计:ViewerState(Empty→Loading→Displaying→Error),按钮状态由状态决定

⑤ 技术方案:语言/框架/工程结构(如 C++/Qt)

⑥ 工程建立:src/ 目录 + 构建 + 测试框架

⑦ 纵向实现(每个切片可验证):
基础设施(FileSystem/ImageDecoder 接口)→ Domain(Image 对象)
→ Algorithm(缩放/旋转算法)→ Service(打开/下一张/删除)
→ View/ViewModel(界面 + 命令)→ 集成 → 测试

每个步骤「做的是什么」都明确:⑥ 是在搭工程,⑦ 是从底层到界面一层层把领域模型落到代码。整条链是领域模型的逆向展开:模型 → 流程(上面的步骤序列)→ 每个步骤可执行、可验证。

2.4 更复杂的逆向:CLI 工具从模型到实现的完整流程

一个 CLI 数据扫描工具(如逆向工具/调试器),其领域模型(FILE/PROCESS/MODULE 等)可逆向成 CLI 工程的完整步骤链(见 06-设计/07-CLI程序的设计顺序、07-工程控制/02):

1
2
3
4
5
6
7
8
9
10
11
12
13
领域模型文档(Process/Module/Symbol/Breakpoint...)
→ 程序类型判断(是 CLI 还是 GUI / 库)
→ CLI 需求分析(要不要参数、要不要交互)
→ 需求点设计(列功能)
→ 业务流程图(用户怎么操作)
→ 功能点设计(一笔一笔拆分)
→ 功能流程图 / 算法设计
→ CLI 能力需求(命令/参数/适配 API)
→ 模块设计(Command/Argument/Application/Adapter/Entry/Output)
→ 接口设计(命令列表/参数列表/异常/Adapter 接口)
→ 数据结构(对象在程序里怎么存)
→ 编码(按步骤实现)
→ 测试(命令测试/参数测试/流程测试/API 集成测试)

这里每一个步骤都是把「领域模型」往前推一步:模型 → 程序类型 → 需求点 → 功能 → 模块 → 接口 → 数据结构 → 代码。每一步的输入、输出、做什么都有明确约定(步骤 | 输入 | 输出 | 说明),所以整套流程可以照做。

同一条逆向规律也适用于其他领域: 角色设计从一句「画一个可爱的少女」出发,也要经过语义 → 外观 → 比例 → 参数 → 草图 → 三视图 → 最终成品的流程——其中「比例」不是普通参数而是关系,被单独抽成一层;素描的核心是「结构/形状/比例/约束 → 画 → 比较 → 修正 → 完成」(模型在每次比较/修正中被验证并更新)。这就是「抽象描述也需要经过领域建模,再转换成一系列设计流程」。

2.5 逆向执行时,模型会被修正

逆向流程不是一次跑完:执行中发现问题(缺对象、状态不对、规则不足)时回到模型修改它,再继续展开。这是一次完整的闭环:

1
2
3
4
5
6
7
8
9
领域模型(正向的产物)
↓ 逆向:展开成流程
流程每一步 → 执行 → 发现偏差

修正模型(加属性/改状态/补规则)

重新展开成新步骤

继续执行 …

模型不是最终答案,而是当前阶段对业务世界的压缩描述——反向执行时不断「比较与修正」,每次修正都会让模型更接近真实(这正是 03-建模与逆向 的双向循环:模型 → 应用 → 验证 → 修正 → 再模型)。


三、领域模型 vs 数据结构

两条线容易混淆,区别在于抽象程度:

1
2
3
领域模型:业务世界的压缩描述(对象、属性、关系、规则、状态)
↓ 设计落地
数据结构:对象在程序里怎么表示(struct、容器、三类分层)
1
2
3
4
5
6
7
调试器:
领域模型:进程 / 模块 / 线程 / 断点 / 符号 以及它们的规则
数据结构:ProcessInfo(Domain)、Vector(Core)、PEHeader(Infrastructure)

图片管理器:
领域模型:Project / Folder / ImageFile / SearchQuery
数据结构:ImageFile struct、Folder 的树、SearchQuery 的哈希索引

领域模型回答「业务世界里有什么」,数据结构回答「这些在程序里怎么存」。 领域模型先于数据结构——先有对象,才谈得上怎么表示。(对应 04 数据结构线:数据结构来自领域对象分析→领域模型链)


四、领域模型 vs 领域对象分析

这两条线也相关,但不同:

1
2
领域模型:把整个业务领域建成一个模型(对象+属性+关系+规则+状态)
领域对象分析:怎么从现实问题里把对象一个一个找出来(分析动作)

领域对象分析是建领域模型的第一步——先会找对象,才能建完整模型。找对象这件事,一般软件和游戏/建模的做法不同,所以领域对象分析又分成两条线(见 06/07 两篇文章)。


五、反向的收益:为什么值得单独建模型

直接写代码 vs 先建模型:

1
2
3
4
5
6
7
8
9
10
11
直接写代码:
界面 → 数据 → 逻辑 纠缠在一起
加功能时改到哪算哪
领域知识散落在代码各处
逆向无着落(没有模型可展开)

先建模型:
所有设计以模型为坐标
逆向展开成流程才有根(模型 → 步骤 → 代码)
加功能 = 在模型里加对象/关系/规则/状态
领域知识集中在一处:可检查、可讨论、可修改

典型收益:底层变化大,模型不变。

1
2
3
调试器换底层实现(win32 → 其他平台)
领域模型不变:进程/模块/断点还是那些
变的是 Infrastructure 层怎么实现它们

六、正向与逆向在这个模型里的位置

1
2
3
正向:现实需求 → 对象 → 属性 → 关系 → 规则+状态 → 领域模型
逆向:模型 → 状态模型 → 用户流程 → 界面/信息架构 → 技术方案
→ 数据结构 → 接口 → 模块 → 实现步骤(每步做什么) → 可运行的

工程里逆向顺序不唯一,但一般是从模型逐步走进时间(先做基础对象→容器→状态→UI→流程→集成)。上面的例子(GUI 图片管理器、CLI 工具、角色设计)就是同一套模型逆向展开成不同流程的案例。


七、和其他线的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
领域模型 ←→ 数据结构(04)
领域模型是业务世界结构,数据结构是程序里的落地

领域模型 ←→ 领域对象分析(06/07)
一般软件:对象从「业务流程/名词」分析出来
游戏/建模:对象从「世界规则/实体」分析出来

领域模型 ←→ 正向建模与逆向应用(03-建模与逆向)
正向:现实 → 抽象 → 模型(业务世界的压缩描述)
逆向:模型 → 流程 → 步骤(模型转为流程,见 03-04 模型转流程)

领域模型 ←→ 设计主题(06-设计)
领域模型是设计流程中靠前的一环:需求 → 领域模型 → 状态 → 交互 → 界面

领域模型 ←→ 从指令到系统主线
主线讲程序内部结构怎么演化
领域模型讲程序对外部业务世界的表示

领域模型 ←→ 认知与语言主题(09)
领域模型 = 语言与具体产物之间的中间表示
(语言 → 领域模型 → 绘制器/执行器)

收束

1
2
3
4
5
6
7
8
9
正向:从业务语言 → 对象(名词)→ 属性 → 关系 → 规则+状态 → 领域模型
反向:领域模型 → 状态模型 → 程序流程 → 模块/接口/数据结构 → 实现步骤
(每一步写清楚做什么、可验证,见 GUI / CLI / 角色设计案例)

vs 数据结构:领域模型是「业务世界结构」,数据结构是「程序落地」
vs 对象分析:领域对象分析是建模型的第一步

收益:模型不动、底层可变;模型可展开成可执行流程;
修正:执行流程时发现模型不足,就修正模型再展开(比较与修正闭环)

先有对业务世界的模型,再把模型展开成一步步的流程去实现。 正向建模型,逆向成流程——这正是一个完整闭环。