--- name: detailed-domain-design description: 定义期第 5 步,细化领域设计:对受影响的领域做 DDD 领域建模、遵循 SOLID 的对象关系模型,有存储时单独设计仓储与 I/O、写入、查询性能。领域影响与选型定好,或人说「细化设计」「仓储怎么设计」「对象关系」时加载。只改一个模块内部实现不加载。 --- # 细化领域设计 三件事有机结合:DDD 领域建模定概念与边界,SOLID 的关系模型定对象怎样协作,仓储设计定数据怎样进出。它们不是三份文档,是同一份设计的三个视角。 领域建模与关系模型每次设计都做,做不做形式化都一样。碰到状态与操作时先填全化表,再翻成这两样,不需要人确认。这一步的产出就是思维模型 `think-model-first` 的模型。设计做完先论证它是对的,再写代码,见「方案论证」。 ## 领域建模 从领域影响分级表的重点领域开始,次要领域只建到履约必需的部分,连带领域只定接口。 - 统一语言:每个概念一个名字,和需求与不变式单里的用法一致。改名要回去改单子。 - 聚合与边界:谁拥有状态,谁发起变化,变化的原子范围是什么。跨聚合的一致性靠事件或流程,不靠跨聚合事务。 - 领域不认识调用方:领域只按外部需求的本质提供服务,不知道调用它的是哪种会话、运行或代理。外部的业务事实由外部凭身份关联。 - 外域概念只建到履约必需:消费的是契约、投影还是引用,在 spec 的跨域五问里定。 方法加载技能 `mattpocock-skills:domain-modeling`。 ## 关系模型 先写类型、签名与模块边界,再写实现。加载技能 `pstack:architect`。设计时核的是: - 深模块:接口小,功能深。加载技能 `mattpocock-skills:codebase-design`。 - 非法状态不可表示,语义基元有类型,外部数据在边界解析。加载技能 `pstack:principle-type-system-discipline`。 - 校验与错误处理集中在系统边界,内部不重复防御。加载技能 `pstack:principle-boundary-discipline`。 - SOLID 按真实的变化轴拆,不为拆而拆。一个类只有一个变化的理由,扩展靠新实现不靠改旧代码,依赖抽象且抽象由使用方拥有。先拆出独立变化的轴,交叉成表逐格看。做法加载技能 `think-orthogonal`。 - 领域决定结构,SOLID 管各部分之间的依赖。有多态、有变化轴、有协作者时用对象;一个全化后的纯函数,最简洁的形状往往是数据加一张表或一个归约函数,硬拆成类只会多出一堆一个方法的类。加载技能 `pstack:principle-model-the-domain`。 ## 从形式化交接表翻译 有全化表或做过 `formal-guard` 的,先把全化表与形式化交接表翻译过来,不照着全化表直接写代码。只有全化表、没做模型检查的,按下表的定义域、全化规则、状态与转移三行翻译。 | 形式化里的 | 翻译成 | | --- | --- | | 状态变量与它们一起要守的不变式 | 聚合与值对象:一起守一条不变式的状态在同一个聚合里,由它独占修改 | | 操作的定义域(前置条件) | 边界上解析成更窄的类型,进了这个类型不再判 | | 定义域外的全化规则 | 显式的结果类型:成功或一组具名失败,不到处判空 | | 状态与转移 | 行为随状态变时用按状态分开的类型或状态模式;只有数据随状态变时用表或归约函数 | | 环境假设(时钟、外部调用、失败) | 端口:接口归使用方,适配器实现 | 翻译是精化:重新组织之后不能放回形式模型排除了的状态。留一张精化对照表,每条不变式写守它的类型或方法,每条全化规则写对应结果类型的哪个变体;证明转契约测试的清单按这张表落到用例上。 翻译也是一次从模型到代码的转手,对照表收紧的就是这一处。转手会丢信息,回路越紧越稳,一块多大由接的一方验得完多少定。做法加载技能 `think-closed-loop`。 ## 方案论证 风险前置:设计错了,越晚发现越贵,损失按已经投入的量放大。所以设计做完、写代码之前,先论证它是对的。论证不过不进下一步。 - 先写三行:先答三问:应然、实然、差异。做法加载技能 `think-ought-vs-is`。 - 走一遍:拿需求的主场景与 spec 失败语义的每一行,在类型与签名上走一遍,每一步写出调用谁、状态怎样变、失败落到哪个结果变体。走不通的地方就是设计缺口。 - 对一遍:每条不变式能指到守它的类型或边界;精化对照表每一行有着落。 - 比一次:工作量大或有两种都说得通的结构时,写出两个候选的类型与签名,按变化轴、依赖方向、改动面比,选一个并写理由。加载技能 `pstack:architect`。 - 验一下:说不准的点用抛弃式原型回答一个具体问题,加载技能 `mattpocock-skills:prototype`;原型不进仓库。 论证的深度跟工作量走:一个模块内的小改动,走一遍主场景就够;跨模块、跨领域或要写几天的,四样都做。论证的每个结论写出依据:走了哪条路径、指到哪个类型、原型回答了什么,说不准的标成没验证。每个结论带证据,证据要真的检验了它声称检验的东西。做法加载技能 `think-evidence`。 ## 仓储设计 有存储需求时单独做,不留给实施期顺手写。没有专门设计时,当前的代理写存储和 I/O 很容易写出性能差的代码。固定回答四问: | 问 | 要定的 | | --- | --- | | 写入路径 | 每次写入的形状、频率、大小;批量还是逐条;是否幂等;失败后重试会不会重复 | | 查询路径 | 每种查询按什么键、返回多大、多频繁;需要哪些索引;有没有全表扫描 | | I/O 形态 | 同步还是异步;流式还是整块;并发写的隔离;事务边界 | | 容量假设 | 一年后的数据量;超过假设时哪条路径先坏 | 仓储接口归领域,实现归基础设施。接口只暴露领域需要的操作,不暴露存储细节。性能目标达到形式化守护的触发条件时,先走 `formal-guard`。 ## 产出 填[细化设计清单](design-template.md)。清单填完的判据:每个重点领域有聚合与边界;每个公开接口有类型与签名;有存储的领域四问都有答案;每条不变式能指到守它的对象或边界;碰到状态与操作的有全化表;做过形式化的有精化对照表;方案论证有记录,走不通的地方已经改掉。 ## 下一步 进入 `formal-docs-landing`,决定哪些进正式文档。