🏗️ 第二篇:架构设计

第3章 领域设计:事件风暴与 27 个限界上下文

用"事件发生的时间线"还原系统该有的样子

3.1 本章导读

第2章给了你模块地图,但 domain 模块内部还有 27 个一级子包(限界上下文)。为什么是 27 个?边界画在哪?本章不罗列结论,而是带你重走一遍设计过程——用事件风暴(Event Storming)的方法,从业务事件出发推导领域边界。读完你会理解:DDD 不是"分包美学",而是让代码结构映射业务结构。

3.2 事件风暴:从事件倒推设计

事件风暴的核心动作:先不管代码,只问"这个系统里会发生哪些事?"把每件事写成橙色便签(领域事件),再往前找触发它的命令(蓝色),往后找做出反应的读模型(绿色),中间是聚合(黄色)和策略(紫色)。

以"用户发一条消息"为例,沿着时间线问下去:

注意:我们全程没提任何类名,只描述了"发生什么"。事件风暴的价值就是延迟技术决策——先把业务流想清楚,再决定代码怎么写。

3.3 27 个限界上下文的全景

把所有事件便签摆在一起,按"哪些事件天然围绕同一个核心对象变化"聚类,就得到了限界上下文。本项目的 27 个可归为六个能力面:

能力面限界上下文核心对象
Agent 执行核心agent, llm, sessionReactLoopAgentPhaseILlmRuntimePortHarnessSessionAggregateSessionLog
任务与目标task, goal, plan, todo, workflow, schedule, jobsHarnessTaskEntityGoalAggregateWorkflowRun
工具与插件tool, plugin, typert, sdkToolRegistryToolCallExecutorRuntimeApprovalGate
运行时与环境runtime, credentials, coderuntime, terminal, lsp, sandbox, e2b, acpModelSettingProfile、各类端口
治理与存储guard, hooks, storage超时策略、Hook 引擎、KV 存储
共享与技能shared, skillHarnessStatusEnumVO 等跨域枚举
💡 为什么拆这么细?

比如 sandbox(本地路径边界)和 e2b(远程沙箱)是两个上下文——它们解决同一类问题但实现策略完全不同,未来可能独立演进。限界上下文的拆分原则:变化的原因不同,就应该分开

3.4 深入解剖:task 域的五子域

不是所有上下文都同样复杂。以治理最关键的 task 域为例,它内部又按职责拆成了五个子域:

子域职责关键对象
submission提交参数校验与过滤TaskSubmissionVO
permission权限矩阵评估(工具 × Profile)PermissionAssessmentVOIPermissionMatrixPort
approval审批决策与审批命令ApprovalDecisionVOApprovalPolicyService
queue任务队列持久化IHarnessTaskQueueRepository
execution会话执行HarnessExecutionServiceIModelExecutionPort

这个拆分让"权限规则变化"只影响 permission,"审批流程变化"只影响 approval——第11章会完整走通这条链路。

3.5 深入解剖:plugin 域的三边界

插件域按"生命周期阶段"拆成三条边界:

Registry(登记处)

PluginRegistryService:安装、元数据管理、启停状态。回答"系统里有哪些插件"。

Bridge(翻译官)

PluginBridgeService:识别包型(JAVA_NATIVE / DSH_NODE_BRIDGE / CODEX / CORDIS),生成绑定计划。回答"这个插件该怎么运行"。

Runtime(执行官)

PluginRuntimeService:启动、停止、查询实例。回答"插件现在跑得怎么样"。

3.6 核心聚合:领域的锚点

每个限界上下文都有一个"锚点"——聚合根,它是外部访问该上下文的唯一入口:

聚合聚合根关键不变量
AgentReactLoopAgent(聚合根服务)单 turn ≤ 50 步;取消必须协作式中断;持有 SessionLog/Inbox/工具注册表
会话HarnessSessionAggregate事件只追加不修改(WAL);SurfaceOp 投影必须幂等
任务HarnessTaskEntity状态机单向流转:CREATED → PENDING_APPROVAL → QUEUED → RUNNING → COMPLETED/FAILED
目标GoalAggregateGoalSnapshotVO 版本单调递增

聚合根的价值在于保护不变量。比如任务状态机,任何地方想把任务从 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 个能力点)

能力点一句话职责关键类详见章节
agentAgent 执行引擎:对话循环、消息编排、系统提示词组装、审批与中断ReactLoopAgentAgentFactoryPhase第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(五子域)任务提交→权限→审批→排队→执行全链路PermissionPolicyServiceApprovalPolicyServiceHarnessExecutionService第11章
goal目标定义与流转,GoalState 状态落库GoalAggregateGoalSnapshotVO第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 个能力点)

能力点一句话职责关键类详见章节
llmLLM 抽象与策略: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 工具族)工具定义、注册、调用与执行核心ToolRegistryToolCallExecutorMatrixRuntimeApprovalGate第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-RPCPluginRuntimeService第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 / conversationSessionLog(内存 WAL)、SessionEventFactory第5章
shared策略路由基础设施:AbstractStrategyRouter(doApply + getNext 责任链)Agent 链/提交链共用第6章
acpACP 协议封装(代理/协作通信接口)AcpServerService第15章
sdk对外 SDK 统一调用入口sdk 层第15章
mcpMCP Server 接入(stdio JSON-RPC 2.0)StdioMcpClientMcpToolAdapter(工具名 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 循环如何一步步驱动模型"思考-行动-观察",以及取消、续写、状态机这些工程细节如何处理。