第3章 领域设计:事件风暴与 27 个限界上下文
用"事件发生的时间线"还原系统该有的样子
3.1 本章导读
第2章给了你模块地图,但 domain 模块内部还有 27 个一级子包(限界上下文)。为什么是 27 个?边界画在哪?本章不罗列结论,而是带你重走一遍设计过程——用事件风暴(Event Storming)的方法,从业务事件出发推导领域边界。读完你会理解:DDD 不是"分包美学",而是让代码结构映射业务结构。
3.2 事件风暴:从事件倒推设计
事件风暴的核心动作:先不管代码,只问"这个系统里会发生哪些事?"把每件事写成橙色便签(领域事件),再往前找触发它的命令(蓝色),往后找做出反应的读模型(绿色),中间是聚合(黄色)和策略(紫色)。
以"用户发一条消息"为例,沿着时间线问下去:
注意:我们全程没提任何类名,只描述了"发生什么"。事件风暴的价值就是延迟技术决策——先把业务流想清楚,再决定代码怎么写。
3.3 27 个限界上下文的全景
把所有事件便签摆在一起,按"哪些事件天然围绕同一个核心对象变化"聚类,就得到了限界上下文。本项目的 27 个可归为六个能力面:
| 能力面 | 限界上下文 | 核心对象 |
|---|---|---|
| Agent 执行核心 | agent, llm, session | ReactLoopAgent、Phase、ILlmRuntimePort、HarnessSessionAggregate、SessionLog |
| 任务与目标 | task, goal, plan, todo, workflow, schedule, jobs | HarnessTaskEntity、GoalAggregate、WorkflowRun |
| 工具与插件 | tool, plugin, typert, sdk | ToolRegistry、ToolCallExecutor、RuntimeApprovalGate |
| 运行时与环境 | runtime, credentials, coderuntime, terminal, lsp, sandbox, e2b, acp | ModelSetting、Profile、各类端口 |
| 治理与存储 | guard, hooks, storage | 超时策略、Hook 引擎、KV 存储 |
| 共享与技能 | shared, skill | HarnessStatusEnumVO 等跨域枚举 |
比如 sandbox(本地路径边界)和 e2b(远程沙箱)是两个上下文——它们解决同一类问题但实现策略完全不同,未来可能独立演进。限界上下文的拆分原则:变化的原因不同,就应该分开。
3.4 深入解剖:task 域的五子域
不是所有上下文都同样复杂。以治理最关键的 task 域为例,它内部又按职责拆成了五个子域:
| 子域 | 职责 | 关键对象 |
|---|---|---|
submission | 提交参数校验与过滤 | TaskSubmissionVO |
permission | 权限矩阵评估(工具 × Profile) | PermissionAssessmentVO、IPermissionMatrixPort |
approval | 审批决策与审批命令 | ApprovalDecisionVO、ApprovalPolicyService |
queue | 任务队列持久化 | IHarnessTaskQueueRepository |
execution | 会话执行 | HarnessExecutionService、IModelExecutionPort |
这个拆分让"权限规则变化"只影响 permission,"审批流程变化"只影响 approval——第11章会完整走通这条链路。
3.5 深入解剖:plugin 域的三边界
插件域按"生命周期阶段"拆成三条边界:
PluginRegistryService:安装、元数据管理、启停状态。回答"系统里有哪些插件"。
PluginBridgeService:识别包型(JAVA_NATIVE / DSH_NODE_BRIDGE / CODEX / CORDIS),生成绑定计划。回答"这个插件该怎么运行"。
PluginRuntimeService:启动、停止、查询实例。回答"插件现在跑得怎么样"。
3.6 核心聚合:领域的锚点
每个限界上下文都有一个"锚点"——聚合根,它是外部访问该上下文的唯一入口:
| 聚合 | 聚合根 | 关键不变量 |
|---|---|---|
| Agent | ReactLoopAgent(聚合根服务) | 单 turn ≤ 50 步;取消必须协作式中断;持有 SessionLog/Inbox/工具注册表 |
| 会话 | HarnessSessionAggregate | 事件只追加不修改(WAL);SurfaceOp 投影必须幂等 |
| 任务 | HarnessTaskEntity | 状态机单向流转:CREATED → PENDING_APPROVAL → QUEUED → RUNNING → COMPLETED/FAILED |
| 目标 | GoalAggregate | GoalSnapshotVO 版本单调递增 |
聚合根的价值在于保护不变量。比如任务状态机,任何地方想把任务从 PENDING_APPROVAL 直接改成 RUNNING,都必须经过聚合根的方法——而聚合根会拒绝(执行入口对 PENDING_APPROVAL 直接抛异常兜底)。这就是 DDD 的防御性设计。
3.7 两条业务主线的交汇
回看整个领域设计,所有上下文最终服务于两条主线:
任务主线的"执行"阶段并没有另起炉灶,而是复用对话主线的 Agent 链路——审批通过后,HarnessExecutionService.executeSession 驱动的是同一个 ReactLoopAgent。这意味着对话和任务共享同一套工具、同一套事件记录、同一套 SSE 推送。
3.8 课程补充:从 6 组到 37 个能力点的逐模块地图
3.3 节的六能力面把 27 个一级包"收拢"了,但工程接手时真正要回答的问题往往是点状的:"审批的评估逻辑在哪个包?""MCP 工具的名字前缀怎么生成?""ask_user_question 归哪个域管?"本节给出一张 37 个能力点(一级包 + 带点号子包)的逐模块地图,作为后续各章的"总索引"——每个能力点标注它所属章节,便于按需跳读。
3.8.1 Agent 执行核心(4 个能力点)
| 能力点 | 一句话职责 | 关键类 | 详见章节 |
|---|---|---|---|
agent | Agent 执行引擎:对话循环、消息编排、系统提示词组装、审批与中断 | ReactLoopAgent、AgentFactory、Phase | 第4章 |
agent.ask | 面向用户的提问/确认交互(弹窗式问题、选项回答) | ask 工具族 + ask_user_question | 第8、11章 |
agent.compaction | 上下文压缩:裁剪、归纳、控制上下文长度 | BasicCompactionEngine(保留最近 1/4,其余 LLM 总结) | 第5章 |
agent.subagent | 子 Agent 注册、创建与启动 | SubagentTool(.join() 阻塞语义) | 第15章 |
3.8.2 任务、目标与规划(7 个能力点)
| 能力点 | 一句话职责 | 关键类 | 详见章节 |
|---|---|---|---|
task(五子域) | 任务提交→权限→审批→排队→执行全链路 | PermissionPolicyService、ApprovalPolicyService、HarnessExecutionService | 第11章 |
goal | 目标定义与流转,GoalState 状态落库 | GoalAggregate、GoalSnapshotVO | 第16章 |
plan | 规划模式:计划状态切换、退出规划模式 | plan 策略节点 | 第16章 |
todo | 待办写入与结果返回 | todo 工具族 | 第16章 |
workflow | 多步骤流程编排、推进与状态管理 | WorkflowService(SSE 只推终态) | 第15章 |
schedule | 定时/延时任务调度 | ScheduleService(⚠️ drive() 无调用方,仓储 InMemory 重启即丢) | 第16章 |
jobs | 后台作业调度与执行管理 | JobRegistryService(newCachedThreadPool 无界) | 第15章 |
3.8.3 LLM 与工具运行时(6 个能力点)
| 能力点 | 一句话职责 | 关键类 | 详见章节 |
|---|---|---|---|
llm | LLM 抽象与策略:ContentBlock/StreamChunk(sealed)、流式端口 | ILlmRuntimePort.stream() 返回 Flow.Publisher | 第4、5章 |
runtime.model | 模型路由与提供方配置管理 | model_setting 表 + 模型路由 | 第12章 |
runtime.setting | 模型配置的查询与命令修改 | /api/harness/settings/models | 第12章 |
runtime.tool | 工具目录与工具选择/解析 | ToolCatalog + 参数解析器 | 第8章 |
runtime.profile | 执行画像/运行模式(headless/web 等) | Profile 策略(web profile 整体需审批) | 第11章 |
tool(12 工具族) | 工具定义、注册、调用与执行核心 | ToolRegistry、ToolCallExecutor、MatrixRuntimeApprovalGate | 第8章 |
3.8.4 插件体系(4 个能力点)
| 能力点 | 一句话职责 | 关键类 | 详见章节 |
|---|---|---|---|
plugin | 插件体系总入口 | 三段式:Registry / Bridge / Runtime | 第9章 |
plugin.registry | 安装、元数据、安装计划与结果、启停 | PluginRegistryService | 第9章 |
plugin.bridge | 识别接入方式并生成绑定计划 | JAVA_NATIVE / DSH_NODE_BRIDGE / DSH_PACKAGE / CODEX / CORDIS | 第9章 |
plugin.runtime | 运行时执行管理:Java 进程内 URLClassLoader 隔离;Node sidecar JSON-RPC | PluginRuntimeService | 第9章 |
3.8.5 治理与平台能力(16 个能力点)
| 能力点 | 一句话职责 | 关键类 / 现状 | 详见章节 |
|---|---|---|---|
guard | 运行守卫:限制危险操作、重复调用提醒、超时校验 | 超时 + 重复调用提醒 | 第8章 |
hooks | 生命周期 Hook(PRE/POST_TOOL_USE 等) | IHookService、CLAUDE_CODE 方言 | 第8章 |
coderuntime | 代码执行运行时支持 | 运行环境端口 | 第16章 |
e2b | 远程沙箱集成(⚠️ 目前仅 StubE2B* 桩实现) | StubE2B* | 第13章 |
credentials | 凭据管理与访问认证数据 | 凭据存储 | 第12章 |
lsp | 语言服务:代码定位、hover、引用查询 | lsp 工具族 | 第16章 |
sandbox | 本地沙箱策略(WORKSPACE_WRITE 限制 cwd) | SandboxPolicyService | 第11章 |
terminal | 终端交互:命令发送、读取、会话快照 | /api/harness/terminal | 第14章 |
storage | 存储抽象:KV、快照、持久化 | storage 端口 | 第5章 |
typert | 类型注册/远程类型协议 | 结果包装协议 | 第9章 |
skill | 技能系统:定义、选择、调用策略与提供方适配 | skill 工具族 | 第16章 |
session(4 子域) | 会话管理:registry / event / log / conversation | SessionLog(内存 WAL)、SessionEventFactory | 第5章 |
shared | 策略路由基础设施:AbstractStrategyRouter(doApply + getNext 责任链) | Agent 链/提交链共用 | 第6章 |
acp | ACP 协议封装(代理/协作通信接口) | AcpServerService | 第15章 |
sdk | 对外 SDK 统一调用入口 | sdk 层 | 第15章 |
mcp | MCP Server 接入(stdio JSON-RPC 2.0) | StdioMcpClient、McpToolAdapter(工具名 mcp__<server>__<tool>) | 第10章 |
上表是"每个模块是什么、在哪个章节讲"。更细的三份 Markdown 姊妹文档:docs/md/domain-design.md(叙事:为什么这么分)、docs/md/domain-case-deep-dive.md(深挖:策略树与 ReactLoopAgent 状态机)、docs/md/domain-modules-reference.md(字典:逐模块速查)。HTML 课程各章则把这三者融进源码讲解。
3.9 小结与下一章预告
本章要点:事件风暴用"发生了什么"倒推领域边界;27 个上下文按变化原因聚类成六大能力面;task 域五子域、plugin 域三边界是"复杂上下文内部再拆分"的范例;聚合根保护不变量(如任务状态机单向流转);对话与任务两条主线在执行阶段汇合。
下一章:地图已经完整。接下来我们钻进最核心的心脏——ReactLoopAgent,看 ReAct 循环如何一步步驱动模型"思考-行动-观察",以及取消、续写、状态机这些工程细节如何处理。