构建新系统时,最容易出现的偏差,是过早讨论框架、数据库和目录结构。技术选型当然重要,但它们应当回答已经明确的问题,而不是代替问题定义。
一套更稳妥的设计顺序是:
目标与约束 → 领域边界 → 质量属性 → 技术决策记录 → 架构与依赖方向 → 目录结构 → 风险验证 → 验收清单
这条主线的重点不是一次性设计出“完美架构”,而是让每项选择都能追溯到目标,让不确定性尽早暴露,并为后续人工开发或 AI 辅助开发提供稳定上下文。
本文使用一个中性示例贯穿说明:为一个约 80 人的组织构建“共享设备预约与借还系统”。员工可以预约会议摄像机、测试手机和便携投影仪,管理员负责设备登记、借出、归还和异常处理。
系统设计的起点不是“采用什么技术”,而是“系统为什么存在,以及什么条件不能被忽略”。
目标应描述业务结果,而不是功能堆叠。示例系统可以定义三个目标:
“增加预约页面”“建立设备表”都不是目标,它们只是可能的实现方式。目标需要说明系统产生的可观察价值。
至少区分直接使用者、业务管理者和系统维护者:
随后为每类用户写出最重要的场景。例如:“员工选择设备和时间段,系统确认无冲突后生成预约记录。”这类场景比抽象的“实现预约模块”更容易转化为规则和测试。
约束会直接影响架构选择。示例系统可能存在以下条件:
这些条件意味着:系统无需一开始采用微服务,也不必引入复杂的消息平台;但身份集成、数据保留和快速交付属于不可回避的设计输入。
输出物 完成本阶段后,应形成一页简短说明:系统目标、主要用户、核心场景、范围边界、已知约束和暂不处理事项。
目标明确后,需要把业务问题拆成职责稳定的领域,而不是直接拆成页面或数据库表。
示例系统中的主要业务对象包括:
对象本身还不够,还要识别围绕它们发生的行为:
可以把示例系统划分为四个领域模块:
身份认证、日志、通知和持久化不属于核心业务领域,它们是支撑多个领域的技术能力。
判断边界是否合理,可以问三个问题:
例如,“同一设备的有效预约时间不能重叠”应由 reservation 负责。页面可以展示冲突提示,数据库可以增加约束,但规则的业务含义不能散落在控制器、页面组件和定时任务中各写一份。
边界也包括“不做什么”。首期可以明确排除:
主动排除低优先级能力,可以防止架构为假设中的未来需求提前付出成本。
功能描述系统“做什么”,质量属性描述系统“做到什么程度”。如果质量属性没有优先级,团队往往会默认同时追求高性能、高可用、强一致、低成本和快速交付,最终无法判断方案是否合适。
示例系统首期可以优先考虑:
高并发并不是首要属性,因为使用规模有限。忽略真实规模而过度优化吞吐量,会挤占更重要的规则验证和审计能力。
质量属性应尽量写成可验证场景:
| 属性 | 场景 | 可验证标准 |
|---|---|---|
| 正确性 | 两名员工同时预约同一设备的重叠时段 | 最多一笔预约成功,另一笔收到明确冲突结果 |
| 可追溯性 | 管理员将设备标记为损坏 | 能查询操作者、操作时间、变更前后状态和备注 |
| 性能 | 员工查询未来七天可用设备 | 正常负载下 95% 请求在 1 秒内返回 |
| 可恢复性 | 数据库发生误删或实例故障 | 关键数据可在约定恢复时间内还原 |
| 安全性 | 普通员工调用设备停用接口 | 请求被拒绝,并记录安全审计信息 |
当属性冲突时,需要预先约定优先级。示例系统可以采用:
预约正确性 > 权限与审计 > 交付速度 > 查询性能 > 技术扩展性
这意味着团队可以接受报表稍慢,却不能接受为了减少一次数据库操作而放松预约冲突控制。
技术决策不应只存在于会议记忆或聊天记录中。建议使用简短的 Architecture Decision Record(ADR),让每项关键选择都可以追溯。
一条 ADR 至少包含:
背景:系统规模小、交付周期短,但预约、借还和统计之间存在明确业务边界。
决策:首期采用模块化单体,在一个应用进程和一个代码仓库中交付,各领域模块通过公开接口协作。
备选项:微服务、无明确模块边界的传统单体。
理由:模块化单体保留清晰边界,同时避免分布式事务、服务发现、跨服务调试和独立部署带来的额外成本。
后果:部署和本地开发较简单;团队必须通过代码规则维护模块边界,不能依赖网络隔离。
复审条件:某一模块出现独立扩缩容、独立发布或数据隔离的明确需求,并且收益大于拆分成本。
背景:仅在应用层执行“先查询、再写入”,无法可靠处理并发请求。
决策:应用层先做友好校验,写入阶段使用事务和数据库约束保证最终正确性。
后果:实现稍复杂,但并发情况下仍能守住核心规则;错误处理需要把数据库冲突转换为可理解的业务结果。
ADR 的价值不在于文档数量,而在于减少重复争论。只有影响长期结构、质量属性或团队协作方式的选择,才值得单独记录。
架构设计的核心不是画出多少层,而是规定依赖可以朝哪个方向流动。
示例系统可以采用以下方向:
各层职责如下:
关键规则是:领域层不依赖 Web 框架、数据库驱动或外部身份平台。外部能力通过接口接入,使业务规则可以独立测试。
以“创建预约”为例:
如果页面组件直接执行 SQL,或者数据库模型直接调用通知服务,依赖方向就被打乱了。短期看似省事,长期会增加测试和替换成本。
模块之间只通过公开能力协作。例如:
对于当前规模,不必为了“解耦”而强制引入消息中间件。进程内事件或明确的应用服务调用已经足够,技术复杂度应与实际问题匹配。
目录结构不是架构本身,但它能强化或破坏既定边界。好的目录让开发者和 AI 都能快速判断:规则应放在哪里、允许依赖谁、修改会影响哪些范围。
示例项目可以采用按领域组织的结构:
目录层级越多,不代表设计越成熟。若一个模块只有少量文件,可以暂时合并层级;当职责和文件数量增长后再拆分。结构的目标是降低定位成本,而不是展示形式上的完整性。
当结构、命名和依赖规则一致时,AI 更容易完成以下判断:
项目说明文件应记录代码无法自行表达的规则,例如禁止的依赖、必要命令、数据安全要求和 ADR 位置,而不是重复介绍每个目录中已经清楚可见的文件。
设计完成后,不应立即把全部功能铺开。先列出高风险假设,并用最低成本验证它们。
示例系统的主要风险可能包括:
| 风险 | 影响 | 验证方式 |
|---|---|---|
| 并发预约产生重复占用 | 核心业务失效 | 编写并发集成测试,验证事务与约束 |
| 组织身份系统集成方式不明确 | 登录与权限无法按期交付 | 制作最小登录原型,验证用户标识和角色字段 |
| 设备管理员实际流程比预期复杂 | 借还模型需要重构 | 用可交互流程与管理员走查典型及异常场景 |
| 报表查询影响日常操作 | 使用体验下降 | 使用模拟数据进行查询计划和响应时间测试 |
| 备份存在但无法恢复 | 数据风险不可控 | 在隔离环境执行一次完整恢复演练 |
验证顺序不应由实现难度决定,而应由“失败后是否会推翻设计”决定。身份集成和并发冲突可能影响整体方案,应在早期验证;按钮样式和报表布局通常可以延后。
技术探针是一段用于回答具体问题的最小实现,例如:
探针的产物可以被丢弃。它的目的不是提前交付功能,而是减少关键未知量。
每次验证后,应更新对应内容:
系统设计不是单向流程。验证结果可能迫使团队返回前面的任一步骤重新判断。
在开始大规模编码前,可以使用以下清单检查设计是否已达到“足够明确”的状态。
构建新系统的设计过程,本质上是不断减少模糊性的过程:先确认目标和限制,再划分业务责任;先确定需要保障的质量,再选择技术;先规定依赖方向,再让目录结构承载这些规则;最后用实验和清单检验设计是否经得起实现。
对人工团队和 AI 辅助开发而言,最有价值的不是复杂术语,而是一组一致、可追溯、可验证的工程约束。当目标、边界、决策和代码结构相互印证时,系统才能在持续变化中保持可理解、可维护和可演进。