编程语言研究书库

第 2 章拆解(中)— Web 应用的四种内部架构,与工单系统的选型推演

Web 应用的四种内部架构,与工单系统的选型推演

这一篇是 02 的下半场的上半场: 前面讲的是「任何系统都可能有的架构」, 这里讲的是「Web 应用自己特有的几种内部架构」——客户端-服务器、MVC、CQRS; 然后是全书项目(工单系统)的立项推演:它做什么、该问哪些问题、 以及怎么用一张打分表把技术选型变成团队都能签字的客观过程。 读完你能拿走两样东西:四种架构的精确边界,和一次可复用的选型流程。

1. 顶层全景

  Web 应用的内部架构
    客户端-服务器(物理分离的两端)→ 本书的默认形态
      MVC(模型-视图-控制器)+ 变体 MVP / MVVM
        CQRS(命令与查询分家)
  工单系统立项
    价值主张 → 业务域(实体/状态/角色)→ 该问的问题清单
      → 决策流程:五个评估维度 + 指标 + 打分表(+加权)

  图说:先定应用内部怎么分角色,再拿具体项目走一遍「怎么选」。

2. 客户端-服务器:前后端分离的架构根据

解决什么问题: Web 的天然形态就是两端物理分离——用户那边一个浏览器(客户端), 机房那边一台服务器。客户端-服务器模式就是给这个事实一个架构地位: 造一个应用,服务一个或多个发请求的客户端1。

书里强调这不是网络专属:数据库内部就是这个模式,Linux 桌面(X Window)也是。 网络通信「可以发生,也可以不发生」1。

落到本书:后端是一个 API 服务器,前端是一个(或多个)客户端,按需发请求。 自己从头造一个监听端口的服务器完全可行(Rust 官方教程就带做一个), 但没必要——「用经过实战检验的方案,在它之上构建,把低层重复的活卸掉」2。

3. MVC:三个角色,不是三个零件

解决什么问题: 端口监听、收发响应这些「没意思的活」交给框架之后, 剩下的是「有意思的部分」怎么分工。MVC(Model-View-Controller,模型-视图-控制器) 是被 Web 和桌面共同认可的最佳实践:三个角色,各管一摊,互不越界3。

角色职责
Model(模型)要操作的数据的封装:领域、概念、能存取的东西、业务逻辑的载体
View(视图)数据的呈现:操作的结果、任何输出,不管介质是什么
Controller(控制器)管输入输出:请求进来先到它;它去取模型、备数据、注入视图,渲染好了随响应发回

三个最容易想错的点(书里特意澄清):

  1. MVC 不是把应用切成三块,是三个「角色」——每个角色可以有任意多个组件: 好几个模型、好几个视图、好几个控制器;一个控制器通常提供多个 action (端点处理函数),对应不同路由和 HTTP 方法,还能按 Accept 头返回不同格式4。
  2. 流动方向: 视图能触发事件 → 控制器处理并更新模型 → 模型更新状态 → 由控制器决定刷新哪个视图5。
  3. 「自称 MVC 的框架其实不止 MVC」: 很多框架覆盖的远不止表示层, 还包括业务层(服务)、持久层(数据库通信)——MVC 只是它们切的第一个角色6。

两个变体(差别都在「视图能不能碰模型」):

  • MVP(Model-View-Presenter): 模型不再注入视图,只被 Presenter 访问; Presenter 把数据整理成视图吃得下的格式。视图彻底碰不到模型——分离更狠7。
  • MVVM(Model-View-ViewModel): ViewModel 表示「从视图角度看有意义的全部数据和行为」。 它不像控制器那样调度,而是接模型、更新自己的状态、接收视图通过「公开动作」发来的调用, 再回头更新模型。比 MVP 更贴身:ViewModel 不为视图准备数据, 它就是视图用的那份数据的封装8。

主线例子走一遍(工单详情页):

  用户点开一张工单
      │  ① 请求到控制器:GET /tickets/42
      ▼
  控制器找模型层取出 id=42 的工单(标题、状态、负责人)
      │  ② 模型是领域数据 + 业务规则的载体(状态能不能这么转,模型说了算)
      ▼
  控制器把数据注进视图,视图渲染成 HTML
      │  ③ 用户看到的是视图;模型和控制器它都看不见
      ▼
  浏览器里出现工单详情页
  ——若点「转交评审」:视图触发事件 → 控制器处理 → 模型改状态 → 视图刷新

  图说:用户只碰视图;规则只住模型;控制器是唯一的中转。
  (id=42、路由写法是为演示编的;「状态流转由模型把关」是 MVC 原文口径。)

4. CQRS:读写分家

先解释词。 CQRS = Command Query Responsibility Segregation, 命令查询职责分离。解决的问题: 同一个数据载体,读的地方和写的地方 要的东西不一样——读列表要标题和状态;写更新只带新状态和修改人。 都塞在一个对象里,它会拖着两边都不需要的赘肉(bloatware)9。

做法:把数据载体按用途分两大类:

类别是什么特点
Command(命令)改变应用状态的指令:带着数据去更新模型只带「这次改变必需的最少数据」
Query(查询)从领域取一份信息:为展示方量身定做的摘要不改状态,只表示状态——哪怕那份信息很复杂

边界: CQRS 不是「必须分库分表」的激进派——轻量做法是「读写用不同的数据结构」, 重量做法(独立读库、事件溯源)本书未展开,属于另一本书的篇幅。

5. 立项:工单系统的价值主张与业务域

这一节是全书项目的「出生证明」。 选型推演之前,先立项。

5.1 价值主张(value proposition)

书里给了一个诚实的声明:真实产品开发里,价值主张由市场和客户定; 这本书是「用预定技术栈演示怎么开发」,所以价值主张从简——但流程照走10。 价值主张要答:客户是谁、问题域是什么、交付什么、对手是谁。

5.2 业务域:工单系统到底管什么

不卖机票! 这个 ticketing application 是 IT 项目管理的工单平台: 工单 = 一个请求、需求或要实现的事;平台管工单的状态在整个开发流程里流转11。

书里列的实体与状态(这是第 4 章数据建模的输入,先记住):

  • 实体: Tickets(工单)、Stages(阶段)、Users(用户);
  • 状态枚举: Created → Estimated → Open → In-progress → In-review → Testing → Business review → Done → Closed(九个)12;
  • 格式问题: 工单格式是预定义静态的,还是用户自定义的动态的? 是统一的,还是每张票可灵活变?12
  • 角色: Administrator、Manager、Contributor12;
  • 约束与访问控制: 对工单要强加什么约束?要哪种访问控制?12

书里最诚实的一段也在这里: 作者明说这些问题故意不提前回答—— 一上来就全答,等于第一页就写一个谁也跟不上的复杂系统。 教学项目的正确走法是 baby steps:范围每一步都收窄,让「当前这一步的技术」 始终清晰、不被未来才引入的概念遮住13。

判断(我们的,不是书里的): 「故意不提前回答」是教学书的特权,真实项目不能照抄—— 但里面有一条能照抄:范围随问题显现而扩大(scope expands when problems manifest)。 「把未来的问题现在就设计上」是过度设计最常见的来源。 如果错,会错在: 如果业务方已确认格式必然动态(比如平台型产品), 拖到后来再改的成本远高于第一天就定;「不提前回答」的前提是「答案真的未知」。

5.3 决策流程:把「我觉得」变成「按尺子量」

解决什么问题: 设计任何东西都要先立评估的尺子,不然讨论会滑向谁的嗓门大。 书里给了五个评估方面(aspect),各自带一串该问的问题14:

方面该问的问题
适应性(Adoptability)这架构多好上手?实现需要的资源现成吗?
敏捷性(Agility)对变化多好反应?需要的话多好掉头?
资源需求(Resource need)对技能、人力、时间、钱的要求?
适合度(Suitability)对目的、客户、期限、资源的契合度?
可维护性(Maintainability)交付之后多好维护?

关键一步:为每个方面定可测量的指标(metrics),把隐含假设、主观意见、人的偏见 尽量挤出去。 比如「资源需求」可以按「要几个独立项目/模块、它们各自的规模倾向」 来量化——行数、文件数、仓库大小,都是客观单位15。

然后填一张表:每个候选架构一行,每个方面一格,按 10 分制或 poor/adequate/good/excellent 打分,和团队一起填16。 书里给了样例数字:Architecture X 在适应性上 9、敏捷性 7、资源需求 8、适合度 6、 可维护性 5;Tech Stack 1 是 4、3、5、6、7——没有哪个候选全面碾压,权衡一目了然17。 还可以加权(权重 = 某个维度的重要倍数):比如这个项目里资源需求格外要命,就给它乘 218。

流程的收尾(也是它真正的价值): 结果基于团队共同的输入, 每个利益相关者都能认——因为它就是每个人贡献的加总。 形式打分表换来的不是正确答案,是「没有暗箱」的决定19。

6. 作者的判断与证据(分开标)

有出处的: MVC/MVP/MVVM 的角色定义、CQRS 的命令/查询划分,都是业内通行定义3789。

作者自己的评估:

  • 五方面评估表是作者推荐的工作法,不是行业标准(业界还有 AHP、ADRs 等其他选型法,本书未提);
  • 「Rust 生态缺全家桶」的反复咏叹贯穿本章,是作者立场;
  • 「九状态枚举」「三角色」是作者为教学项目设计的具体方案,不是领域标准。

7. 边界与局限

  • 打分表防偏见,不防烂尺子: 维度选错、权重乱设,表填得再工整也是精致地犯错。
  • CQRS 只给了轻量口径: 事件溯源、读写分库这些重量形态没展开。
  • 工单需求「故意不答」: 第 11 章(动态 schema)才会回头收拾「格式动态」这个伏笔;
  • 读者若照搬到真实项目,要自己补齐需求确认的环节。

8. 可带走的

  1. 客户端-服务器是 Web 的默认形态: 后端一个 API 服务器,前端一个或多个客户端; 别自己造轮子,在实战检验的组件上构建。
  2. MVC 是三个角色不是三块零件: 模型=数据+规则,视图=呈现,控制器=中转; 变体之争的核心只有一个问题——「视图能不能碰模型」。
  3. CQRS 的轻量版值得默认采用: 读的结构和写的结构分开定义,两边都不拖赘肉。
  4. 选型先立尺子: 五方面(适应性/敏捷性/资源/适合度/可维护性)+ 可测指标 + 团队填表, 可以加权;价值在「没有暗箱」。
  5. 范围随问题显现而扩大: 但前提是答案真的未知;已知的未来需求不该拖。

9. 原文地图

主题原书章原文位置
客户端-服务器与后端 APISelection of a Tech Stacktext/12-fm-the-architecture-frenzy.txt:443(搜「serves one or more clients」)、:446(搜「battle-tested」)
MVC 三角色职责同上text/12-fm-the-architecture-frenzy.txt:452(搜「best practice」)、:459(搜「encapsulation」)、:467(搜「channeled to controllers」)
控制器可多 action / 按 Accept 分格式同上text/12-fm-the-architecture-frenzy.txt:471(搜「Accept header」)
「自称 MVC 的框架不止 MVC」同上text/12-fm-the-architecture-frenzy.txt:504(搜「advertise themselves」)
MVP 变体同上text/12-fm-the-architecture-frenzy.txt:488(搜「Model-View-Presenter」)
MVVM 变体同上text/12-fm-the-architecture-frenzy.txt:493(搜「ViewModel」)
CQRS 命令/查询定义同上text/12-fm-the-architecture-frenzy.txt:515(搜「Command Query Responsibility Segregation」)、:519(搜「instruction to change」)、:522(搜「does not alter the state」)
价值主张声明同上text/12-fm-the-architecture-frenzy.txt:545(搜「predefined tech stack」)
工单业务域与实体同上text/12-fm-the-architecture-frenzy.txt:551(搜「tracking development」)、:555(搜「Tickets」)
九状态枚举同上text/12-fm-the-architecture-frenzy.txt:563(搜「Created」)
格式/角色问题清单同上text/12-fm-the-architecture-frenzy.txt:586(搜「Predefined static format」)、:601(搜「Administrator」)
「故意不提前回答」与 baby steps同上text/12-fm-the-architecture-frenzy.txt:619(搜「as narrow as possible」)、:629(搜「baby steps」)
五方面评估同上text/12-fm-the-architecture-frenzy.txt:650(搜「Adaptability」)、:656(搜「Resource need」)、:662(搜「Maintainability」)
指标与量化同上text/12-fm-the-architecture-frenzy.txt:669(搜「metrics」)、:672(搜「lines of code」)
打分表样例同上text/12-fm-the-architecture-frenzy.txt:675(搜「scale of 10」)、:724(搜「9」)
加权同上text/12-fm-the-architecture-frenzy.txt:821(搜「factor of two」)
「结果基于团队贡献」同上text/12-fm-the-architecture-frenzy.txt:824(搜「team contribution」)

3.1 MVC 变体的判定细节(视图到底能不能碰模型)

MVP 的彻底性: 模型不注入视图之后,视图连「直接摸数据」的机会都没有了—— 数据准备全在 Presenter 手里,视图只负责画。书里那个对照点很精: MVP 比 MVC 分离更狠,代价是每写一个视图都要多写一个 Presenter 层20。

MVVM 与 MVP 的微妙差别(容易混,书里特意分开):MVP 的 Presenter 为视图准备数据;MVVM 的 ViewModel 就是视图用的那份数据的封装—— 它不「准备」数据,它「是」数据,还附带视图能调的动作。 「ViewModel 不接模型、更新自己的状态、接收视图通过公开动作发来的调用, 再回头更新模型」——三个动词接力21。

Controller 的 workload 书里也没藏: 一个 controller 提供多个 action (端点处理函数),响应不同路由、不同 HTTP 方法;还能按 Accept 头回不同 内容类型——同一张票,浏览器要 HTML、脚本要 JSON,一个 controller 全包22。

4.1 价值主张之后的「作者自白」

书里第 2 章这段诚实得罕见,值得整段接住:**定义价值主张(客户是谁、问题域、 交付什么、对手是谁)本来是产品开发的工作;而本书是「用预定技术栈演示开发」, 所以价值主张从简——「作者作为作者的视角,不可避免地与作为 Rust 工程师、 产品负责人或项目经理的你的视角不同」。但流程照走:接下来仍要带你走一遍 决策过程和摆在面前的选择。读任何技术书都该带上这个警觉:示范项目的 「需求」是被安排好的,真实项目的需求是要你去发现的23。

5.1 数据建模的更多细节(backlog 的排序哲学)

backlog 的有序不是小事:「这个队列,或 backlog,可以自由增长,谁都能往里 加任务;但列表必须排得有意义。」 排序的两个基准:创建时间(升序—— 最老的最紧急;或降序——新的在前,社交媒体的时间线就是这么排的); 以及优先级(prioritizing)——手动凌驾于任何自动排序之上。 「理想情况是两种都尊重:既按创建时间、又按优先级。」24

状态集的两次进化(书里明写的演化式证据):初版六态(Created / Ready / Started / Reviewing / Testing / Done / Closed)是「项目经理最关心的里程碑」; 讨论到 process view 时意识到两件事——「拉进处理」和「真正开工」不是 一回事(于是诞生 SELECTED),且有些态对总览无关紧要。模型是被需求 逼出来的,不是想出来的;这条在第 4 章还会重演一次(加 user 外键靠 DBeaver 的图才抓到疏漏)25。

TicketUpdate 实体的存在理由(状态历史不是顺手加的):「经理们想看 一张票的状态随时间怎么变,好在有疑问时追问」——审计需求的直接产物。 它同时预告了第 4 章的删除规则:「有状态变更记录的票不许删」, 历史完整性有了数据载体26。

5.4 工作流场景全记录(设计文档的原始形态)

书里把四个场景写成了可以照抄的模板,每个都是「动作 → 表单 → 提交 → 读回」:

  1. 建票: 点创建按钮 → 表单出现 → 填写 → 提交 → 执行一次读操作 (返回上一视图 + 展示票的详情视图);
  2. 改票: 打开详情 → 点更新 → 更新表单 → 改值 → 提交 → 读回(同 1.e);
  3. 评论: 详情页点「加评论」 → 评论表单 → 提交 → 读操作刷新详情;
  4. 看板操作: process view 里对票执行状态更新操作(具体交互留待定义)→ 读操作刷新看板。

「还有更多场景,但这些足够尝到『写场景定义工作流』的滋味了;实现阶段会 回来对照、评估、更新设计。」「写场景」就是把验收标准提前写成故事—— 第 6 章 BDD 的 feature 文件,正是这种场景的机器可读版27。

5.5 决策流程的完整示范(打分表怎么用)

把第 5.3 节的流程走成一次真实的会:

  会前:PO 和架构师定下五个评估方面 + 各自的可测指标
      (如「资源需求」= 独立项目数 × 平均仓库体积)
  会上:三种候选(单体加好模块 / SOA / 微服务)逐行打分(10 分制):
      Architecture X    9 / 7 / 8 / 6 / 5
      Tech Stack 1      4 / 3 / 5 / 6 / 7
      Another candidate 3 / 4 / 7 / 7 / 9
  会后:加权(本例资源需求 ×2) → 算总分明细存底 → 全员签字
  ——分数本身不神奇;神奇的是「每个数字背后站着谁、按哪把尺子」。

  图说:这套流程的输出不是答案,是一份没有暗箱的决定记录。
  (样例分数出自原书 Table 2.1;候选名为原文占位。)

5.6 业务域全账本(第 4 章数据建模的输入清单)

把建模节里散落的「实体属性表」补全成可直接抄作业的账本(第 4 章的迁移 就是照这张表建的):28

实体属性关系
ProjectSummary、Deadline、Owner、Active(进行中标记)一个项目含多张票;归属于一个负责人
TicketTitle、Description、Priority、State、Project 引用、Owner属于一个项目;有多条评论、多条变更日志
TicketUpdateTicket 引用、PreviousState、NextState、Timestamp、Owner挂在一张票下;只增不改
CommentText、Owner、Timestamp、Ticket 引用挂在一张票下
UserName、Password、Role(Manager/Developer)建票、开项目、发评论的发起者

建模时主动砍掉的粒度(书里明说「读者可加练」的清单):登录名与 真实姓名的区分、更多实体属性、「进一步的资格限定」——「对演示没有增益, 所以不做;但鼓励读者把粒度磨得更细」。MVP 的砍法不是「没想到」, 是「想到了也先不」29。

两个建模判断的原文依据:

  • backlog 不成实体: 「不需要存——它是动态视图,随时能从其他实体的 数据查出来」(CREATED 状态的票按序排列即是);
  • 数据模型中立: 「换 MariaDB 还是 MongoDB 都成立,技术决定可以 后置」——这是第 4 章敢先 SQLite 后 MariaDB 的底气30。

5.7 决策五维度的追问清单(开会版)

把五个 aspect 各配一串会上真问得出的话(第 2 章原文问题清单的展开):31

维度会上怎么问
Adaptability上手这套架构要多久?需要的资源(人/库/环境)现成吗?
Agility需求变了多好掉头?方向错了多好转弯?
Resource need技能门槛多高?要几个项目/模块?时间成本?额外花钱吗?
Suitability对这批客户、这个期限、这批资源,它合适吗?
Maintainability交付六个月后,改起来顺手吗?

指标化的两个示范(原文给的): 「资源需求」可量化为「独立项目/模块数 + 它们的规模倾向(行数、文件数、平均仓库体积)」;「适应性」可量化为 「团队现有技能与所需技能的重合度」。能量化就别用形容词—— 这条纪律在此后的每一次选型(框架、缓存、编排)都适用32。

2.1 客户端-服务器的一次归位

这个模式「在 Web 之前就在」:数据库内部是它,Linux 桌面的 X Window 也是它——「服务器是角色不是机器」这句话在第 9 章容器化还会再听到一次。 它的架构贡献就一条:把「谁发起」变成明确的设计决定——浏览器客户端

  • API 服务器的分野,让两端可以独立演化、独立伸缩33。

6.1 四种内部架构的选型判据(一张对照定终身)

问客户端-服务器MVC 系CQRS
解决什么两端的物理与逻辑分野表现层内部的分工读与写的形态分化
粒度系统级表现层内部数据载体级
耦合点网络协议controller 的中转共享的领域模型
何时引入Web 应用默认有任何 UI 时读写需求明显分化时

「不是三选一」的提醒: 它们是叠层关系——客户端-服务器定两端, MVC 管前端(或服务端渲染页)内部,CQRS 管数据载体的形状; 真实的工单系统四层全在用34。

9. 原文地图补充

主题原书章原文位置
MVP/MVVM 对照Selection of a Tech Stacktext/12-fm-the-architecture-frenzy.txt:488(搜「Model-View-Presenter」)、:493(搜「ViewModel」)
权限矩阵的删除约束同上text/34-fm-designing-the-application.txt:253(搜「Can be deleted」)
打分表样例分同上text/12-fm-the-architecture-frenzy.txt:724(搜「9」)

5.8 权限矩阵的逐条规则账(原文 Table 的完整复述)

第 5.3 节的矩阵收得太紧,这里把原书逐条约束完整复述(每条都能在第 4 章 找到数据层的落点):35

Create: 任何时刻可建票(无条件前置);评论只能在处理阶段添加—— 「票还没被拉进流程时,讨论对象尚不存在」。

Read 的四个视图: Backlog(CREATED 票,按优先级+创建日期排序)、 Process view(SELECTED/STARTED/REVIEWING/TESTING/DONE 五态的在制票, 「动态视图」)、Ticket detail(单票全字段+评论;Developer 与 Manager 都需要)、Ticket history(状态变更时间线,「大概对所有角色开放」)。

Update 的角色分野: Developer 只能在处理阶段改状态(「把票推过 流程」);Manager 可在票关闭前改票(「管内容」);历史数据谁也不许改 ——「None: 防止伪造记录」。这条是 TicketUpdate 只增不改的设计根据。

Delete 的五条细规: 项目(不含票时可删);票(有过状态变更记录就 不许删——历史完整性优先于清理冲动);评论(本人可删,但有回复就 不许删——对话链不可断);TicketUpdate(永远不可经应用删除); 用户(仅 Manager 可删)。36

这套规则的自我节制(原文口吻): 「数据模型对历史记录支持有限, 所以只追踪状态变更;其余更新收紧给 Manager」——规则不是因为做不到 才少,是因为收益不够才砍;「加更多特性有没人用的风险,而开发资源稀缺」。

5.9 决策流程的防操纵设计(加权与签字的用意)

书里给的两个机制细节,都是防「会开完没人认账」的:37

  • 加权: 「比如资源需求对我们格外关键——两倍重要,就把它乘二」—— 权重必须在打分之前公开定,打完再加权就是操纵;
  • 团队共填: 「和团队一起填这张表」——「结果基于团队共同的输入, 每个利益相关者都能接受,因为它就是每个人贡献的加总」。

这套流程的适用边界(我们的补充): 它防的是暗箱与推诿,防不了 烂尺子;维度选错(比如漏了「合规」)、权重乱设,表填得再工整也是 精致地犯错。所以它前面必须有一步「维度从哪来」的讨论——书里用 「列出方面与问题清单」给了示范38。

2.2 MVC 的流动方向再演示(以主线例子的一步为例)

  小李在工单详情页点「转交评审」(视图触发事件)
      → 控制器收到:PUT /tickets/42,新状态 REVIEWING
      → 控制器找模型:校验迁移合法 → 改状态 → 写 TicketUpdate 日志
      → 控制器选视图:重渲染详情页(状态徽章变了)

  图说:视图不碰模型;控制器是唯一的中转与决策者。
  (对照第 5.1 节的全景图:MVC 只是分层架构中「表现层内部」的分工。)

7.1 章节地图的完整版(03/04 两篇的分工边界)

本篇(03)与 04 的边界一句话:03 讲「怎么决定」,04 讲「决定什么」。 03 的四块——客户端-服务器、MVC 系、CQRS、工单立项——全部是「思考的 形状」:角色怎么分、视图能不能碰模型、读写要不要分家、需求怎么落成 问题清单与打分表;到了 04,候选才登场:数据库三视角、六军规、五框架、 REST/OpenAPI/GraphQL、前端三选项。先学思考的形状,再看货架上的货, 顺序反了就会拿着锤子找钉子——这也是第 5 章(本组 08 篇)锤子定律 的第一次预演。39

本篇内部的一条暗线: 四种内部架构其实是同一个问题的四个答案—— 「变化来了,该往哪儿隔离」。客户端-服务器把两端的变化隔离在网络协议 两侧;MVC 把「界面要变」与「规则要变」隔离在三个角色之间;CQRS 把 「读的样子要变」与「写的样子要变」隔离成两套载体;而工单的立项流程, 则是把「业务要变」隔离成一份可更新的设计文档。学架构就是学隔离, 学隔离就是预测变化的落点——这句可以作为本篇的卷尾语。40

5.10 建模故事的完整时间线(按「问题出现 → 方案落地」排)

把第 4 章建模节的事件按因果串起来,这是「演化式设计」的一次完整回放:41

  1. 业务说要「跟踪开发」 → 立项:工单平台,管状态流转(不是卖机票);
  2. 任务要排队 → backlog:有序、无限增长、按创建时间+优先级排序;
  3. 经理要「看见在制品」 → process view;且发现「拉进来 ≠ 开工」 → 诞生 SELECTED 态;
  4. 要追责与复盘 → TicketUpdate 实体(状态变更日志,只增不改);
  5. 要讨论 → Comment 实体(挂在票下);
  6. 要分权 → 三角色与权限矩阵(历史数据谁也不许改);
  7. 技术跟着收:五实体建表(迁移)、关系补全(DBeaver 抓虫)、 删除规则(有历史的票不许删)。

这条时间线的读法: 每个实体都能回答「谁问了一个什么问题」; 反过来,任何一个「再加点东西」的冲动,都该先回答「它对应谁的问题」—— 第 13 章「没人在用的功能不要加」就是这条纪律的复读。42

5.11 打分表之外的三个选型案例(同一尺子的三次复用)

原书的五维度尺子在本篇之内就用了三次(我们的对账):43

  • 表示法三选一(08 篇 §2): 「专业化↔可复用性成反比」其实就是 Suitability(适合度)+ Resource need 的合体判断;
  • 搜索方案三选一(08 篇 §7): contains / Elasticsearch / Toql 的 取舍,是把 Agility(模式变了多好掉头)与 Resource need(多养一个服务) 放上天平;
  • 实时三档(16 篇 §3–5): 轮询/SSE/WebSocket 的选择 = 延迟要求 (Suitability)对负载代价(Resource need)的权衡。

尺子的可迁移性(我们的判断): 它不限于架构选型——任何「候选 ≥2、 利益相关者 ≥2」的决定(买 SaaS、选云服务、定团队结构)都能套这张表; 价值不在表,在「打分前公开维度与权重」这个动作。44

12. 本篇卷尾(一句话版)

四种内部架构回答同一个问题——「变化来了往哪儿隔离」;工单立项回答 另一个问题——「需求怎么变成可打分的问题清单」。两个答案合起来, 就是「想清楚再动手」的全部手艺;04 篇接手摆货。45

13. 本篇卷尾补记(立项问题清单的日常用法)

第 2 章立的那组问题(格式静态还是动态?角色有几种?要什么访问控制?) 不必等立项才问——每加一个功能、每接一个新客户,都可以把清单翻出来 重新过一遍。它真正的价值是让「需求未知」显性化:答不出的问题 就是下一轮调研的题目,而不是开发里的地雷。46

14. 本篇卷尾补记(打分表复用提醒)

五维度尺子记得在「非架构」场景里复用:买 SaaS、选云区域、定团队结构, 凡候选 ≥2 且利益相关者 ≥2 的决定都能套;维度与权重先公开,表后填—— 这套顺序比表本身更防操纵。47

15. 本篇卷尾补记(问题清单的保鲜)

立项问题清单(格式?角色?约束?访问控制?)要贴在项目 Wiki 的首页, 而不是立项文档的第 N 页——答案会变,问题常新;每次需求评审把它 翻出来,答不出的条目自动成为下一轮调研的题目。48

16. 本篇卷尾补记(工作流场景的保存格式)

四个场景脚本(建票/改票/评论/看板)建议按原书的「动作 → 表单 → 提交 → 读回」格式存在项目 Wiki——它们就是第 6 章 BDD feature 文件的人类语言 底稿,一份内容两处复用。49

17. 本篇卷尾补记(场景脚本的交接)

四个场景脚本交给实现阶段时,就是第 4 章 CRUD 端点的验收清单—— 每句「读回」都对应一个 GET 端点的返回值。50

Footnotes

  1. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:443,搜「serves one or more clients」;:443,搜「Network communication may or may not take place」)。 ↩ ↩2

  2. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:446,搜「battle-tested solutions」)。 ↩

  3. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:452,搜「separation of concerns」)。 ↩ ↩2

  4. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:471,搜「Accept header」)。 ↩

  5. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:481,搜「trigger events」)。 ↩

  6. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:504,搜「advertise themselves as an MVC framework」)。 ↩

  7. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:488,搜「Model-View-Presenter」)。 ↩ ↩2

  8. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:493,搜「ViewModel represents」)。 ↩ ↩2

  9. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:515,搜「bloatware」)。 ↩ ↩2

  10. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:545,搜「demonstrating the development」)。 ↩

  11. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:551,搜「tracking development」)。 ↩

  12. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:555,搜「Tickets」;:563,搜「Created」;:586,搜「Predefined static format」;:604,搜「Administrator」)。 ↩ ↩2 ↩3 ↩4

  13. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:629,搜「baby steps」)。 ↩

  14. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:650,搜「Adaptability」)。 ↩

  15. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:669,搜「eliminate implied assumptions」;:672,搜「lines of code」)。 ↩

  16. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:675,搜「fill together with the team」)。 ↩

  17. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:719,搜「Architecture X」;:724,搜「9」)。样例分数为原书表格内容。 ↩

  18. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:821,搜「factor of two」)。 ↩

  19. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:824,搜「team contribution」)。 ↩

  20. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:488,搜「Model-View-Presenter」)。 ↩

  21. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:493,搜「ViewModel represents」)。 ↩

  22. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:471,搜「Accept header」)。 ↩

  23. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:545,搜「unavoidably different」)。 ↩

  24. 出处:「The architecture frenzy」(text/34-fm-designing-the-application.txt:10,搜「free to grow」)、:10(搜「prioritizing」)。 ↩

  25. 出处:「The architecture frenzy」(text/34-fm-designing-the-application.txt:39,搜「process view」)、:202(搜「evolutionary approach」)。 ↩

  26. 出处:「The architecture frenzy」(text/34-fm-designing-the-application.txt:65,搜「changed over time」)。 ↩

  27. 出处:「The architecture frenzy」(text/34-fm-designing-the-application.txt:274,搜「User clicks a button」)、:337(搜「process view」)、:345(搜「enough to get a taste」)。 ↩

  28. 出处:「The architecture frenzy」(text/34-fm-designing-the-application.txt:75,搜「Project: Any new process」)、:86(搜「Ticket: We deal with tickets」)、:99(搜「TicketUpdate: Log entry」)、:110(搜「Comment: Optional textual remarks」)、:119(搜「User: The user of the system」)。 ↩

  29. 出处:「The architecture frenzy」(text/34-fm-designing-the-application.txt:129,搜「encouraged to improve the granularity」)。 ↩

  30. 出处:「The architecture frenzy」(text/34-fm-designing-the-application.txt:148,搜「generated dynamically」)、:158(搜「not bound to relational databases」)。 ↩

  31. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:650,搜「Adaptability」)、:656(搜「Resource need」)、:662(搜「Maintainability」)。 ↩

  32. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:672,搜「lines of code」)、:669(搜「metrics」)。 ↩

  33. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:443,搜「serves one or more clients」)与(text/74-fm-containerization-with-docker.txt:13,搜「not a machine but a role」)。 ↩

  34. 叠层关系为我们的归纳;各模式定义见原书相应小节。 ↩

  35. 逐条复述依据:「Designing the application」(text/34-fm-designing-the-application.txt:220,搜「Create: Users can create」)、:225(搜「Backlog」)、:246(搜「Developer: Can update」)、:253(搜「Project: Can be deleted」)。 ↩

  36. 出处:「Designing the application」(text/34-fm-designing-the-application.txt:255,搜「Ticket: Can be deleted」)、:259(搜「Cannot be deleted via the application」)。 ↩

  37. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:821,搜「factor of two」)、:824(搜「team contribution」)。 ↩

  38. 出处:「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:646,搜「list some such aspects」)。 ↩

  39. 分工为我们的写作划分;锤子定律见 text/42-fm-introduction.txt:1527。 ↩

  40. 卷尾语为我们的归纳;四种架构的定义见本篇 §2–§4。 ↩

  41. 时间线依据:「Designing the application」(text/34-fm-designing-the-application.txt:10 起全节)与「The architecture frenzy」(text/12-fm-the-architecture-frenzy.txt:551 起)的实体清单。 ↩

  42. 读法与纪律为我们的归纳;「没人用的功能不要加」见 text/84-fm-introduction.txt:20。 ↩

  43. 三次复用为我们的全书对账;各案见对应拆解篇。 ↩

  44. 可迁移性为我们的判断;尺子定义见 text/12-fm-the-architecture-frenzy.txt:650 起。 ↩

  45. 卷尾为我们的归纳;隔离命题见本篇 §7.1,问题清单见 §5.3。 ↩

  46. 补记为我们的用法建议;问题清单见原书「Designing the application」一节(text/34-fm-designing-the-application.txt:75 起)。 ↩

  47. 复用提醒见本篇 §5.11 与 §5.7;权重纪律见原书 text/12-fm-the-architecture-frenzy.txt:821。 ↩

  48. 保鲜建议为我们的产品化整理;清单见原书「Designing the application」(text/34-fm-designing-the-application.txt:75 起)。 ↩

  49. 保存建议见本篇 §5.4;BDD 对应见 09-testing-and-qa.md §7。 ↩

  50. 交接为我们的对账;场景见本篇 §5.4,端点见 06-backend-workflows.md §7.2。 ↩

On this page

Web 应用的四种内部架构,与工单系统的选型推演1. 顶层全景2. 客户端-服务器:前后端分离的架构根据3. MVC:三个角色,不是三个零件4. CQRS:读写分家5. 立项:工单系统的价值主张与业务域5.1 价值主张(value proposition)5.2 业务域:工单系统到底管什么5.3 决策流程:把「我觉得」变成「按尺子量」6. 作者的判断与证据(分开标)7. 边界与局限8. 可带走的9. 原文地图3.1 MVC 变体的判定细节(视图到底能不能碰模型)4.1 价值主张之后的「作者自白」5.1 数据建模的更多细节(backlog 的排序哲学)5.4 工作流场景全记录(设计文档的原始形态)5.5 决策流程的完整示范(打分表怎么用)5.6 业务域全账本(第 4 章数据建模的输入清单)5.7 决策五维度的追问清单(开会版)2.1 客户端-服务器的一次归位6.1 四种内部架构的选型判据(一张对照定终身)9. 原文地图补充5.8 权限矩阵的逐条规则账(原文 Table 的完整复述)5.9 决策流程的防操纵设计(加权与签字的用意)2.2 MVC 的流动方向再演示(以主线例子的一步为例)7.1 章节地图的完整版(03/04 两篇的分工边界)5.10 建模故事的完整时间线(按「问题出现 → 方案落地」排)5.11 打分表之外的三个选型案例(同一尺子的三次复用)12. 本篇卷尾(一句话版)13. 本篇卷尾补记(立项问题清单的日常用法)14. 本篇卷尾补记(打分表复用提醒)15. 本篇卷尾补记(问题清单的保鲜)16. 本篇卷尾补记(工作流场景的保存格式)17. 本篇卷尾补记(场景脚本的交接)Footnotes