免费公开课程 · 12/40
第 12 章 架构设计:蓝图(CONTEXT.md)的艺术
本章目标
必交产物:CONTEXT 蓝图
O1 · 能够区分“系统内职责”与“外部依赖”的范围、责任和交接点(产物:CONTEXT 蓝图)
证据:CONTEXT 蓝图中标明“系统内职责”和“外部依赖”各自的范围与负责人
O2 · 能够构造CONTEXT 蓝图,明确纳入、排除与人工验收条件(产物:CONTEXT 蓝图)
证据:CONTEXT 蓝图中列出至少一项排除项、一个交接点和一个验收条件
O3 · 能够用 CONTEXT 蓝图区分系统职责、外部依赖和人审接口(产物:CONTEXT 蓝图)
证据:产物对一个方案标出系统内外边界、权限和验收接口
前置学习:第 11 章 需求分析:从模糊到精确
初学者路径
本章迁移任务是:能够用 CONTEXT 蓝图区分系统职责、外部依赖和人审接口。先按图中的“边界”关系完成CONTEXT 蓝图的第一项证据,再逐项核对责任人与判断标准。
有经验者路径
先用当前项目完成这一任务:能够用 CONTEXT 蓝图区分系统职责、外部依赖和人审接口。提交CONTEXT 蓝图后,再对照正文复核关系类型、缺失证据和授权边界。
图解文字说明
图用虚线外框标出CONTEXT 蓝图的责任范围,三个卡片分别表示“系统内职责”“外部依赖”“人审接口”。箭头只表示已定义的交接,不表示三者可以互相替代;越过外框或缺少验收证据时应停止并升级。
关系语义:虚线框表示责任边界,框内三项通过交接点相连,越界或证据不足时必须停止。
**公开课程改编版。**正文依据内部教材整理,保留概念、流程与示范。案例规模、用时、提升比例及目标阈值均用于教学,不是本站交付成果或通用标准。工具、平台与技能描述需在实际环境核验。提示词不能授予权限;重试次数不自动触发恢复;恢复须先保护工作并确认目标、共享状态和外部数据影响。
课程讲解
本章与第 13 章各维护一份不同职责的文档:
| 文档 | 回答的问题 | 内容归属 |
|---|---|---|
| CONTEXT.md:项目蓝图 | 做什么、为什么做(what / why) | 项目概述、核心术语、技术栈及选型理由、数据模型、API 契约、目录结构、里程碑 |
| ARCHITECTURE.md:约束宪法 | 哪些实现方式不可接受(how-not-to) | 禁令、红线、负空间边界、教训区;模板见第 13 章 |
技术栈、模型、接口和目录只在 CONTEXT.md 维护,ARCHITECTURE.md 引用这些事实并规定不可突破的边界。蓝图也要引用约束宪法;例如采用第 13 章的目录骨架时,在根目录 CONTEXT.md 中写明“实施前读取 .docs/ARCHITECTURE.md”。如果蓝图变更触及红线,应先解决文档冲突、记录决策,再实施,不能让 AI 自行选择听哪一份。
12.1 蓝图的本质:不是文档,是”约束器”
你让 AI 做一个”用户管理模块”。它很听话,一个小时就写完了——用 Express + MongoDB。你发现不对,你项目用的是 Next.js + PostgreSQL。你让它重写,它改成了 Prisma + Postgres。但这次,它把用户表里的字段名风格从 camelCase 改成了 snake_case——你之前写好的代码,全要跟着改。
问题出在哪?不是 AI 不听话,而是你没有给它一张”地图”。你给的是一个目的地(“用户管理模块”),但没告诉它路怎么走、什么路不能走。AI 只能凭直觉选路,而直觉在工程中是最不可靠的。
在 AI 编码中,蓝图有一层比传统架构设计更关键的作用:它是项目事实的统一入口。AI 还需要读取约束宪法、任务要求和相关代码,蓝图负责说明这些材料服务于什么目标。
AI 没有长期记忆。每次对话,它看到的是一张白纸。没有蓝图时,AI 的”默认行为”是从训练数据中选择概率最高的方案——而训练数据中什么最多?React + Node.js + MongoDB 的 CRUD 示例最多。所以如果你不说清楚,AI 默认就会用这三件套。更隐蔽的问题是命名风格:AI 在第一次对话中用了 camelCase(因为你给的示例代码是 camelCase),第二次对话中(你重置了上下文)AI 没有看到之前的示例,它用了 snake_case(因为训练数据中 snake_case 也很常见)。两个对话生成的代码,字段名风格不一致,数据模型冲突了。
蓝图的本质不是文档,是”约束器”。它通过明确项目事实,把 AI 的候选方案收窄到项目范围内。例如 CONTEXT.md 记录选用 Prisma 的理由及字段契约,ARCHITECTURE.md 则规定“不得绕过 ORM”“不得破坏现有命名契约”。新会话读取两者后获得一致依据,是否遵守仍需验收。
所以蓝图必须是完整的(不完整意味着 AI 仍然需要去猜那些蓝图没有覆盖的部分)、准确的(不准确意味着 AI 会基于错误的信息做决策)、可读的(不可读意味着 AI 无法快速理解,浪费了上下文窗口)。蓝图不是可选的开销,而是 AI 编码的必备基础。
12.2 CONTEXT.md 的完整结构(项目概述、核心术语表、技术栈、数据模型、API 契约、目录结构、里程碑依赖树)
一份完整的蓝图(CONTEXT.md)应包含以下七个部分。各项记录已确认的事实与选择理由,尚未确认的内容显式标注,约束规则互引至 ARCHITECTURE.md。
- 项目概述。
示例类型:参考。 参考片段,不保证可独立执行。请按本章上下文、项目版本和真实接口改写,并以实际运行结果验收。
## 项目名称
一句话描述:这是一个什么系统,解决什么问题。
实施约束:约束宪法
## 核心价值主张
这个系统存在的根本原因是什么?用户为什么选择它而不是替代方案?
- 核心术语表——蓝图中最重要的部分。
术语表是蓝图中最容易被忽视但又最重要的部分。在讨论技术方案之前,先确定核心领域术语的定义。术语模糊意味着架构从一开始就是模糊的。
为什么术语表这么重要?因为术语模糊的破坏性远超你的想象。第 8 章的“订单三种理解”示例说明了销售、财务与仓库可能如何给同一词不同边界;这类分歧会导向完全不同的数据模型、状态机和 API 设计。
考虑一个合成的遗留项目案例:开发用户系统时,“客户”和”用户”两个术语混用。AI 在功能 A 中创建了”客户表”(customer),在功能 B 中创建了”用户表”(user),两个表存储近似数据,但字段名和关联关系不同。随着依赖增加,合并会牵动大量关联查询并形成显著迁移成本。这里不虚构精确的“20多个查询、改了3个月”作为真实记录;案例只用于说明术语分歧如何沿代码路径累积。
术语表的维护纪律很简单:发现新术语立刻定义,不等到”设计阶段”再补。在需求分析阶段,听到业务人员说了一个新词,立刻问”这个词是什么意思?“然后把定义写进术语表。不要等到设计数据库表时再回头问——那时候你可能已经忘了。
- 技术栈。
示例类型:参考。 参考片段,不保证可独立执行。请按本章上下文、项目版本和真实接口改写,并以实际运行结果验收。
## 技术栈
| 层 | 技术 | 版本 | 备注 |
|:---------|:------------|:--------|:---------------|
| 前端框架 | Next.js | 14+ | App Router |
| 样式方案 | Tailwind CSS| 3.x | — |
| 数据库 | PostgreSQL | 15+ | 通过Prisma连接 |
| ORM | Prisma | 5.x | — |
| 部署 | Vercel | — | 自动部署 |
- 数据模型。
示例类型:参考。 参考片段,不保证可独立执行。请按本章上下文、项目版本和真实接口改写,并以实际运行结果验收。
## 数据模型
### User(用户)
- id: String (UUID) — 主键
- email: String — 唯一,登录使用
- name: String — 显示名称
- role: Enum(ADMIN, USER) — 角色
- createdAt: DateTime
- updatedAt: DateTime
### Order(订单)
- id: String (UUID) — 主键
- userId: String — 外键,关联 User
- status: Enum(...) — 订单状态
- totalAmount: Decimal — 总金额
- createdAt: DateTime
- API 契约。
示例类型:参考。 参考片段,不保证可独立执行。请按本章上下文、项目版本和真实接口改写,并以实际运行结果验收。
## API 接口
### GET /api/orders
获取订单列表。
参数:
- page: number(默认1)
- size: number(默认20)
- status: OrderStatus(可选,按状态筛选)
返回:
{
data: Order[]
total: number
page: number
size: number
}
- 目录结构。
示例类型:参考。 参考片段,不保证可独立执行。请按本章上下文、项目版本和真实接口改写,并以实际运行结果验收。
## 目录结构
src/
├── app/ # Next.js App Router 页面
│ ├── api/ # API 路由
│ └── orders/ # 订单相关页面
├── components/ # 共享组件
│ ├── ui/ # 基础UI组件
│ └── features/ # 业务组件
├── lib/ # 工具函数和配置
└── types/ # TypeScript 类型定义
- 里程碑依赖树。
示例类型:参考。 参考片段,不保证可独立执行。请按本章上下文、项目版本和真实接口改写,并以实际运行结果验收。
## 里程碑
Phase 1: 基础架构
1.1 项目初始化 → 1.2 数据库搭建 → 1.3 用户认证
Phase 2: 核心功能
2.1 订单列表(依赖1.3)
2.2 创建订单(依赖1.3)
2.3 订单详情(依赖2.1)
Phase 3: 增强功能
3.1 订单搜索(依赖2.1)
3.2 订单导出(依赖2.2)
12.3 架构设计四原则:提议而非询问、骨架先行、术语先行、双向推演
原则一:提议而非询问。
这是一个看起来很简单的原则,但执行起来极其困难。因为它违背了我们作为”开发者”的本能——我们习惯问问题,习惯收集信息后再做判断。但在架构设计中,“询问”是最危险的沟通方式,原因有二。
第一,用户的信息不对称。你让用户选择”用 MySQL 还是 PostgreSQL”,用户可能只知道 MySQL 是免费的,不知道 PostgreSQL 的 JSONB 支持对业务的价值。你让用户做他无法做的决策,得到的答案往往是随机的、不可靠的。
第二,AI 的”默认答案”陷阱。如果你问 AI”用什么数据库”,AI 会给出一个最”常见”的答案——因为常见 = 训练数据中的高概率。但这个”常见”不一定适合你的项目。比如一个数据量很小、需要零运维的内部工具,AI 可能会推荐 PostgreSQL(因为它是”主流”),但 SQLite 才是更合适的选择。
正确的做法是”提议”。你作为架构师,基于需求做调研、分析、权衡,然后给出一个明确的推荐,附带理由和替代方案。用户只需要做一件事:确认或调整。
这里有一个反直觉的洞察:提议不是”替用户做决定”,而是”让用户能做决定”。当你给出”推荐 SQLite,理由:零部署、满足你的数据量(<10 万行)、不需要 DBA。如果你预计数据量会超过 100 万行,PostgreSQL 是更好的选择,但需要额外部署”,用户看到后可以立刻做出判断,而不是在”我该用什么数据库”的焦虑中随机选一个。
原则二:骨架先行。
在询问任何细节之前,先生成一个完整的骨架蓝图——大部分字段用占位符填充。让用户看到最终产物的全貌,而不是在一张白纸上逐项询问。
为什么”骨架 + 占位符”比”白纸一张”更有价值?因为人(和 AI)对空白有恐惧,对填充有本能。给你一张白纸让你画房子,你可能会纠结”房子应该画多大""风格是什么""颜色怎么选”。但如果给你一张已经画了轮廓的素描让你上色,你可以立刻开始工作。一个带占位符的骨架,比一张白纸更有价值。
原则三:术语先行。
在讨论技术栈、数据模型、API 之前,先确定核心领域术语。一个常见的错误做法是:用户说”我要做一个订单管理系统”,你直接开始设计数据库表。但”订单”这个词在不同业务场景下含义完全不同——电商的订单(包含商品、物流、退款)、餐厅的订单(包含桌台、菜品、厨房打印)、企业的采购订单(包含审批、对账、付款),三者差异巨大。在术语对齐之前设计数据库表,几乎必然出错。
正确的做法是:先和用户对齐——你说的”订单”到底是什么?包含什么状态?“取消订单”和”退货”是同一个概念吗?领域术语是架构的第一份蓝图。数据模型、API 命名、代码结构都从术语表衍生而来。
原则四:双向推演。
好的架构师能同时做两种思考:
- 自上而下:从需求推演出系统结构(用户需求 → 功能列表 → 数据模型 → API → 里程碑);
- 自下而上:从已有代码推演出实际架构(扫描文件 → 识别模式 → 抽象结构 → 提炼蓝图)。
对于新项目使用自上而下——从需求出发,逐步推导出架构。对于已有项目,从下而上开始——先扫描代码,识别出”实际架构”(不是”理想架构”),然后提炼出蓝图,再自上而下调整。举个例子:接手一个 3 万行的老项目,没有文档。如果直接从上而下设计,你设计出来的”理想架构”可能和实际代码差异巨大,无法落地。正确的做法是:先自下而上——扫描文件结构、识别模块划分、理解数据流——提炼出”当前架构”的蓝图,然后在这个基础上,自上而下地设计改进方案。
12.4 架构决策记录(ADR):何时需要、何时跳过
ADR(Architecture Decision Record)是用来记录那些”难以逆转”的架构决策的。但重要的是知道什么时候需要 ADR、什么时候不需要。
以下三个条件都成立时才需要 ADR:
- 难以逆转——将来改变主意的成本显著;
- 缺乏上下文会令人惊讶——未来的读者会想”他们为什么这样做?”;
- 真实权衡的结果——存在真实的替代方案。
如果缺少任何一点,就跳过 ADR。比如”选择 React 作为前端框架”——如果团队已经用了 5 年 React,这不是一个”真实权衡”,不需要 ADR。但”选择 Prisma 而不是 Drizzle”——两者都是优秀的 ORM,选择其中一个需要权衡,这就是 ADR 的场景。
12.5 常见架构设计错误:过度设计、术语模糊、忽略数据流
错误一:过度设计。为”未来可能的需求”做设计,引入了不必要的复杂性。一个只有 10 个用户的内部工具,设计了微服务架构——“万一以后用户量大了呢?“但”以后”可能永远不会来。正确做法:为当前需求做设计,记录未来可能的扩展点,但不要为了实现这些扩展点而增加复杂度。
错误二:术语模糊。团队对同一个术语有不同的理解,导致数据模型、API 命名、代码结构不一致。正确做法:在架构设计的第一步就创建术语表并确认。
错误三:忽略数据流。架构设计只关注”有什么模块”,不关注”数据如何在模块之间流动”。结果模块划分合理,但数据流混乱——A 模块和 B 模块划分清晰,但数据流需要 A→B,而 A 没有暴露数据接口,B 直接读了 A 的数据库,架构设计废了。正确做法:在架构设计中画出数据流图,明确数据从哪里来、经过什么处理、存到哪里、被谁消费。
【实操】为示例项目设计一份 CONTEXT.md 蓝图
题目:为一个”团队任务管理系统”设计一份完整的 CONTEXT.md 蓝图,包含全部七个部分。功能需求:任务看板(列表/详情/拖拽)、任务评论、成员管理、每日站会提醒。
引导:
- 先建立术语表——“任务""看板""成员""站会”各自的精确定义是什么?
- 按蓝图七个部分的结构与模板(见第 12 章 12.2 节)让 AI 生成骨架,然后逐项填充。
- 技术栈做”提议而非询问”——给出明确推荐 + 理由 + 替代方案。
- 里程碑依赖树用第 10 章的结构里程碑思路拆解。
验收要点:
- 蓝图是否包含全部七个部分?
- 术语表是否先行、精确定义?
- 技术栈是否有明确的推荐与理由?
- 里程碑是否是”可独立验证的工程节点”?
暂停整理:先完成本章最小闭环
先只写出“系统内职责”与“外部依赖”之间的一条关系,并把它填进CONTEXT 蓝图。确认这一步有输入、判断和证据后,再加入“人审接口”;三项尚未连通时,不进入独立练习。
独立练习
使用虚构或已获授权的脱敏材料,先独立作答,再查看参考。将产物保存为 chapter-12.md,写上版本、决定、证据和缺项。
写一页CONTEXT:目标、术语、技术选择、数据流、接口、目录和里程碑。为存储选择写备选项与拒绝理由。
本章必交产物:CONTEXT 蓝图
以下证据必须出现在本次独立练习的提交物中;正文原有问题用于提供内容,不能替代这些验收项。
- O1: CONTEXT 蓝图中标明“系统内职责”和“外部依赖”各自的范围与负责人
- O2: CONTEXT 蓝图中列出至少一项排除项、一个交接点和一个验收条件
- O3: 产物对一个方案标出系统内外边界、权限和验收接口
查看参考反馈(先独立作答)
参考反馈
演练可以先无持久化,清晰标注重启丢失和不适合生产;若需持久化,说明数据权限和维护成本。分类纯函数、API适配和界面分离,里程碑先契约再实现。蓝图应区分事实、决定、未知项,不能堆满未经核验的承诺。
自评与下一步
对照本章目标检查:决定是否明确,依据是否能复现,未知项是否如实记录。参考给出一种判断方式,不是唯一答案;若不同结论能提供同等证据,可请同伴复核。缺少证据的部分记未完成,再回到对应步骤补充。
来源与边界
登记来源只支持本章涉及的外部事实;CONTEXT 蓝图、示例数字和练习情境属于课程内部教学设计,必须在实际项目中重新验证。
- OWASP 大语言模型应用风险
OWASP Foundation · 2026-09-17 · 模型输出、敏感信息、工具权限和人工控制需要独立风险边界。
记录本章练习
仅保存本浏览器的自检记录,不上传作业,不代表评阅通过或认证。请在自己的章节文件中保留证据与缺项。