第4章 Web表示层

拆分请求控制、领域模型与视图生成,比较控制器和视图模式的变化轴。

学习目标

  • 能区分输入控制、领域模型调用和视图渲染三种 Web 表示层责任
  • 能用 TypeScript 编写不携带业务规则的页面控制器,并返回稳定的视图模型
  • 能根据页面数量、路由共享、视图变体和导航状态选择 Page Controller 或 Front Controller

为什么第4章 Web表示层值得单独学习

第4章 Web表示层的核心不是套用某个框架 API,而是决定请求如何被解释、业务模型如何被调用以及响应如何被生成。订单后台多视图通常同时包含列表、详情和导出三种表现;如果每个模板都直接访问数据库并计算业务规则,一次字段变化就会穿过路由、查询和页面,责任边界也无法测试。

需要把输入协议转换为应用可以理解的意图。本页以 2024 年中文版公开目录限定第4章的范围,并依据 Martin Fowler 的作者图书页和模式目录独立重写;目录只决定要讲什么,案例、实验、判断题和答案均为本课程原创。

先建立直觉:问题、机制与代价

应当知道如何展示数据,而不必知道数据从哪个数据库表读取。

负责领域知识,

负责入口协调;这样可以分别替换模板、路由和领域实现。

对第4章 Web表示层,优先比较的替代路线是:页面少且逻辑局部时采用 Page Controller,需要统一认证、路由和错误处理时采用 Front Controller;多视图共享业务数据时让控制器调用模型并生成专用 View Model。若实验只能显示页面渲染成功,不能指出请求映射、模型调用和视图生成的责任,就不能据此选择控制器模式。

目录单元到教学证据

第4章 Web表示层

第4章 Web表示层要求把“请求如何变成响应”变成可观察的架构决定:先标出输入解析、用例调用、视图选择和错误处理的所有者,再用订单后台多视图的路由测试与渲染结果验证决定。只有模板替换不会迫使业务逻辑重写,表示层边界才真正成立。

4.1 视图模式

4.1 视图模式关注如何组织展示与数据转换。模板视图适合把稳定的视图模型变成 HTML,转换视图适合把同一模型映射到不同输出;视图不应直接查询数据库或决定订单授信规则,否则页面变化会反向污染领域边界。

4.2 输入控制器模式

4.2 输入控制器模式关注请求入口的粒度与共享范围。Page Controller 让每个页面拥有清晰的输入处理,Front Controller 则把认证、路由、错误和通用上下文集中起来;选择取决于共享程度,而不是控制器类的数量。

4.3 进一步阅读

4.3 进一步阅读应把本章的 MVC 责任链连接到页面控制器、前端控制器、模板视图、转换视图和两步视图等模式。下一步阅读要回到当前应用的路由数量、视图变体和导航状态,验证模式是否解决了真实变化,而不是因为名称相近就增加抽象层。

专属代码案例:订单后台多视图

把订单后台多视图切成“请求 → 输入控制 → 模型查询 → View Model → 响应”五个观察点。第4章 Web表示层的设计草案必须写出谁拥有状态、谁作出业务决定、失败怎样传播,以及请求映射、控制器、模型、视图和导航中哪个指标最先提示当前方案不再适用。

设计记录采用五个可换行字段:单元键为 poeaa24-chapter-04-web-presentation;模式族为 web;裁决是“控制器协调输入与用例,模型拥有业务规则,视图只消费稳定的 View Model”;观测项包括请求映射、控制器、模型、视图、导航;拒绝条件是“模板直接访问数据库或在展示逻辑中修改领域状态”。

先预测:如果订单详情页改成 CSV 导出,哪些职责应该变化,哪些职责应该保持不变?先写下控制器输入、模型查询和视图转换的边界,再阅读实现;验证时应能说明同一订单数据如何被两个输出视图消费而不复制折扣规则。

type OrderSummary = {
  id: string;
  customerName: string;
  totalCents: number;
  status: "open" | "paid";
};
 
type OrderRequest = {
  userId: string;
  orderId: string;
  format: "html" | "csv";
};
 
interface OrderQuery {
  findVisibleOrder(userId: string, orderId: string): OrderSummary | undefined;
}
 
type OrderViewModel = {
  heading: string;
  customer: string;
  total: string;
  status: string;
};
 
function toOrderViewModel(order: OrderSummary): OrderViewModel {
  return {
    heading: `Order ${order.id}`,
    customer: order.customerName,
    total: `$${(order.totalCents / 100).toFixed(2)}`,
    status: order.status === "paid" ? "Paid" : "Open",
  };
}
 
class OrderPageController {
  constructor(private readonly orders: OrderQuery) {}
 
  handle(request: OrderRequest): OrderViewModel | "not-found" {
    const order = this.orders.findVisibleOrder(request.userId, request.orderId);
    return order ? toOrderViewModel(order) : "not-found";
  }
}

这段代码让控制器负责输入和查询协调,让 OrderSummary 代表模型输出,让 OrderViewModel 作为 HTML 或 CSV 视图的稳定边界。真实应用还要由路由层处理身份与错误响应;不能把折扣计算、数据库写入和模板分支继续塞进 handle

MVC 职责分离解剖

Web 表示层的核心是把请求处理切成三个职责:Controller 解析输入并协调,Model 封装业务数据与规则,View 负责渲染。三者通过明确接口通信,各自可以独立演化;下面的图把用户操作、模型调用、视图生成和响应路径放在同一张证据图中。

MVC:职责分离的三角协作修改状态通知变化读取状态用户操作Model业务数据 + 规则View展示 + 渲染Controller解析输入 + 协调请求处理路径1. HTTP 请求到达2. Controller 解析参数3. 调用 Model 业务逻辑4. Model 返回结果5. 选择 View 模板6. View 渲染 HTML7. 响应返回客户端Model:不知道谁在展示View:不知道数据从哪来Controller:不知道如何渲染分离的价值:Model 可独立测试,View 可独立替换,Controller 可独立路由
MVC 把请求处理切成三个职责:Controller 解析输入并协调,Model 封装业务数据和规则, View 负责渲染展示。三者通过明确接口通信,各自可独立演化。

选择控制器策略时,Page Controller 适合页面少且逻辑简单的场景;Front Controller 适合需要统一入口、认证、路由和错误处理的场景。无论选择哪一种,控制器都不应绕过模型直接修改持久化状态。

控制器策略决策图

先看是否存在大量共享的认证、路由和错误处理,再看页面是否拥有独立输入流程;没有共享证据时保持 Page Controller 的局部性,有共享证据时集中到 Front Controller。视图变化则通过 View Model 或转换视图隔离,而不是把条件分支复制到每个模板。

控制器策略:局部页面还是共享入口页面少、输入局部Page Controller每页独立处理输入失败路径局部可测共享入口证据Front Controller认证 + 路由 + 错误共享上下文可观测输出变体View Model模板 / CSV 转换不复制业务规则拒绝条件:模板查库、控制器持有全部业务规则控制器共享基础设施,模型拥有规则,视图消费稳定输入
控制器策略由共享范围和输出变化推动;统一入口不等于把所有页面逻辑集中到一个类。

选择与拒绝矩阵

评审问题选择表示层方案的证据应拒绝当前方案的信号
责任请求解析、模型调用和视图生成各有所有者模板直接查询数据库或改写领域状态
变化View Model 隔离 HTML、CSV 和详情页变化新增一个输出格式要复制全部业务规则
共享认证、路由和错误处理的共享范围明确为了统一而把所有页面逻辑塞进一个巨型控制器
导航页面流转和失败路径可测试、可观测只能从最终 HTML 猜测请求映射与错误来源

常见误区

可验证练习

练习

本组练习覆盖 4.1 视图模式、4.2 输入控制器模式和 4.3 进一步阅读,并要求把请求映射、控制器、模型、视图和导航映射到订单后台多视图。

问题 1:隔离输出。 同一订单需要 HTML 详情和 CSV 导出,两个模板都在计算订单总额,应该如何调整?

问题 2:选择控制器。 只有三个互不共享输入流程的页面,是否必须先建立 Front Controller?

问题 3:处理下一步阅读。 阅读两步视图或转换视图模式时,应该用什么证据判断它们是否适合当前后台?

名词解释

名词解释

本章出现的专业名词,用大白话再讲一遍。

控制器

负责解析请求、选择用例或领域行为、准备视图输入并决定响应路径的入口对象。

视图

把领域或查询结果转换成用户可见结构的输出组件,例如模板、页面组件或导出格式。

模型

表示业务状态、行为和不变量的应用对象或查询模型,供控制器调用而不是由模板直接改写。

输入控制器

把请求参数、身份和路由转换成一个用例调用,并把结果交给视图或响应格式的协调模式。

本章小结

掌握第4章 Web表示层的标志不是记住 MVC 三个字母,而是能在订单后台多视图中解释“请求 → 输入控制 → 模型查询 → View Model → 响应”的责任链。学习者应利用请求映射、控制器、模型、视图和导航作出可证伪的选择,并在模板直接访问数据库或复制业务规则时明确拒绝当前实现。

前后导航

来源与改写范围

资料与写作方式声明

本章以Martin Fowler《企业应用架构模式》与公开模式目录权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…