软件设计之道-“田”字模型

B站影视 港台电影 2025-09-23 19:39 1

摘要:“老王烤得一手好羊肉串,以前生意全靠他一个人。客人来了,冲他喊:‘王哥,来一手肥点的,多辣!’ 老王心里记下,转身就从冰箱里拿肉,现穿现烤,最后吆喝一嗓子:‘您的串好嘞!’,亲自端上去。

“兄弟们,大家好。今天我们来聊一个可能会改变我们工作习惯的新模型。在开始讲枯燥的理论之前,我先给大家讲个故事,关于咱们公司楼下那家‘老王烧烤’。”

--- 第一部分:老王的传统手艺(过去的我们)

“老王烤得一手好羊肉串,以前生意全靠他一个人。客人来了,冲他喊:‘王哥,来一手肥点的,多辣!’ 老王心里记下,转身就从冰箱里拿肉,现穿现烤,最后吆喝一嗓子:‘您的串好嘞!’,亲自端上去。

这个过程,简单直接,但问题很大:

全凭脑子记: 人一多,谁点了啥、要不要葱花香菜,根本记不住,老上错菜。效率极低: 他一个人又当点单员、又当厨师、 #技术分享又当传菜员,忙得脚不沾地,一晚上也接待不了几桌。无法规模化: 老王病了或者想开分店,这套手艺根本复制不了。

这像不像我们过去的某些项目?

需求全靠产品经理口头传达。一个高手程序员从头写到尾,又是前端又是后台又是数据库。代码耦合严重,只有他自己能维护。项目无法复用,再来一个类似的需求,还得重头再写一遍。

--- 第二部分:老王的数字化危机(我们遇到的痛点)

“后来老王上了‘美团’,生意爆火,订单蜂拥而至。问题也更大了:

输入不标准: 有客人备注‘微辣’,有写‘一点点辣’,有写‘辣度一级’。老王懵了,这‘一点点’到底是多少?处理不过来: 订单打印出来贴了一墙,老王根本看不过来,经常烤糊或者漏单。输出会出错: 外卖小哥挤在门口问:‘订单尾号6789是谁的?’,店员手忙脚乱地找,经常拿错。

这像不像我们系统间的混乱交互?

上游系统传过来的数据格式千奇百怪,我们要写一大堆 if...else 来适配。系统内部逻辑复杂,一量上来就性能瓶颈,频繁宕机。输出的接口、文档混乱,调用方抱怨连连,联调成本极高。

--- 第三部分:老王的智慧转型(“田”字模型的精髓)

“老王痛定思痛,花了点时间做了一个 革命性的改革

flowchart TD A[顾客下单] --> B[统一输入
标准点单码] B --> C[标准化处理
中央厨房] C --> D[统一输出
标准化出餐] subgraph A [第一横:输入标准化] B endsubgraph B [第二横:处理专业化] C endsubgraph C [第三横:输出标准化] D endB --> C C --> D【定义输入 - 第一横】 :他搞了个 标准化点单码 。每个菜品都有一个唯一码,辣度固定为【不辣、微辣、中辣、特辣】四档。客人 只能 这么选。从源头上保证了输入的统一和清晰。【优化处理 - 第二横】 :他在后院搭了个 中央厨房 。前厅店员只负责接单,把标准订单传给后厨。后厨分工明确:有人专门穿串,有人专门掌火,有人专门刷酱。 各司其职,专业高效【明确输出 - 第三横】 :他设立了 统一的出餐口 。每个烤好的套餐都用一个标有 订单号 的盘子装好,放在出餐台上。外卖小哥来了,直接报号取餐,又快又准。

“改革之后,老王的生意做到了这条街第一。”

--- 第四部分:从烧烤到代码(我们的启示)

“兄弟们,我们写代码、做系统,和老王开烧烤店,本质是一模一样的 !”

那个 ‘标准化点单码’ ,就是我们的 输入规范接口契约 。我们必须先和上游定义清楚,他们到底‘喂’什么数据给我们。那个 ‘中央厨房’专业分工 ,就是我们的 应用内部设计 。我们要把自己的系统拆分成一个个职责单一的模块(比如订单服务、库存服务、支付服务),而不是一个什么都干的‘大泥球’。那个 ‘统一的出餐口’订单号 ,就是我们的 输出结果 。我们给前端、给其他系统提供的数据和接口,必须标准、清晰、稳定。

“如果我们不学老王,而是在美团订单涌来时,还在那手忙脚乱地靠脑子记‘一点点辣’,那结局只能是 系统崩溃,全员加班,客户投诉 。”

“所以,我们今天要介绍的这套 ‘田’字模型 ,其实就是把我们代码世界的‘中央厨房’给建起来。”

第一横:输入 -> 搞清楚 ‘谁给我喂数据?’ (定义好你的点单码)第二横:处理 -> 想清楚 ‘我拿到数据后要干什么?’ (设计好你的中央厨房和流水线)第三横:输出 -> 明确 ‘我干完活要交出什么?’ (准备好你的出餐口)

“从今天起,我希望大家在动手写代码前,都像老王设计他的中央厨房一样,先花时间做好设计。先定义规矩,再开始干活 。这样,我们才能从‘手工作坊’式的开发,转变为‘现代化工厂’式的生产,最终交付稳定、高效、可扩展的系统!”

“好,接下来,我们看看具体该怎么操作……”

指引: 列出所有触发此功能执行或为此功能提供数据的来源。思考:数据从哪来?是什么格式?怎么传给我?

| 来源类型 | 来源方(哪个应用/用户) | 触发方式(API 调用/消息事件/定时任务) | 协议与数据格式(HTTP/Event, JSON/?) | 核心数据字段说明 | | ---

| 例如:内部应用 | 前端 Web 页面 | HTTP POST API 调用 | application/json | { "productId": 123, "quantity": 2 } | | 例如:消息队列 | 订单服务 | 监听 order.created 事件 | JSON | { "orderId": "O001", "userId": "U001" } | | ... | ... | ... | ... | ... |

指引: 列出这个功能执行后所产生的所有结果和副作用。思考:我要返回什么?要通知谁?要改变什么状态?

| 输出类型 | 消费者(返回给谁) | 输出方式 | 数据格式 | 核心数据字段说明 | | ---

| 同步响应 | 前端 Web 页面 | HTTP Response | application/json | { "orderId": "O001", "status": "created" } | | ... | ... | ... | ... | ... |

| 输出类型 | 消费者(通知给谁) | 输出方式 | 数据格式 | 核心数据字段说明 | | ---

| 领域事件 | 积分服务、消息推送服务 | 发布 OrderCreatedEvent 消息 | JSON | { "orderId": "O001", "userId": "U001" } | | 数据库变更 | -

| ... | ... | ... | ... | ... |

flowchart LR A[接收输入请求] --> B{校验权限与数据} B -- 校验失败 --> C[返回错误信息] B -- 校验成功 --> D[执行核心业务逻辑-创建订单,扣减库存] D --> E[持久化数据到数据库] E --> F[发布订单创建事件] F --> G[返回成功响应]

首先,告诉开发团队,我们要改变“接到需求就建表写 CRUD”的习惯。每一个应用或功能模块都应该被看作一个 有明确边界、有输入输出、可复用 的“积木”(或称为“组件”、“服务”)。

这个“积木”的内部运作,必须遵循“ ”字模型:

flowchart TD subgraph SecondHorizontal [第二横:我们的应用/服务-处理] direction LR Process[内部业务逻辑
与数据处理] endInput[第一横:输入
上游应用/数据/事件] --> Process Process --> Output[第三横:输出
页面/接口/事件/数据]subgraph Roles [三竖角色-贯穿始终] direction TB R1[产品-定义做什么] R2[技术-设计怎么做] R3[用户/测试-验证好不好用] endInput -.-> Roles Process -.-> Roles Output -.-> Roles

架构师的指令:

“兄弟们,以后做任何一个新功能,先别敲代码。第一步,先把它框起来,问自己三个问题:

谁给我喂数据? (Input)

我拿到数据后要干什么? (Process)

我干完活要交出什么? (Output)

把这三个问题和产品、测试聊清楚,形成文档,我们再开始设计。”

接下来,为团队引入“清单文化”,将“田”字模型的抽象思维转化为具体的开发任务和设计文档。这个流程及其产出物如下图所示:

flowchart LR A[需求评审
PRD/原型] --> B[第一步:创建功能场景清单] B --> C[第二步: 细化输入,处理,输出清单] C --> D[第三步: 推导出数据库与集成设计] D --> E[第四步: 架构师评审清单] E -- 通过 --> F[编码实现] E -- 驳回 --> Bsubgraph B [第一步:创建功能场景清单] direction LR B1[梳理所有场景
明确每个场景的 I-P-O] endsubgraph C [第二步: 细化输入,处理,输出清单] direction LR C1[输入清单] C2[处理清单] C3[输出清单] endsubgraph D [第三步: 推导出数据库与集成设计] direction LR D1[数据库设计清单] D2[集成清单
接口,事件,菜单] end

架构师的指令:

“我们的开发流程要增加一个设计环节。每个人都要负责填写这几张表:

1. 功能场景清单 (I-P-O清单):

| 场景编号 | 场景名称 | 输入 (Input) | 处理 (Process) | 输出 (Output) |

| --- | --- | --- | --- | --- |

| C001 | 用户下单 | 商品ID、用户ID、收货地址 | 1. 校验库存
2. 计算价格
3. 创建订单 | 订单ID、支付链接 |

| ... | ... | ... | ... | ... |

2. 输入/处理/输出详细清单: 对每个场景的I、P、O进行细化。 3. 数据库设计清单: 表中的每个字段都必须能在上述清单中找到来源和目的。 4. 集成清单: 明确需要暴露哪些接口、监听哪些事件。

我会评审这些清单,通过之后才能开始写代码。这是我们的‘开工许可证’。”

教导团队如何识别和设计“可复用积木”。关键在于分析“输入”的多样性,并在“处理”层做好抽象

flowchart TD subgraph A [不可复用的烟囱式设计-避免] A1[App 1] --> B1[定制化服务A
仅理解App1的数据] B1 --> C1[输出] endsubgraph B [可复用的积木式设计-追求] direction TB D1[App 1] --> E D2[App 2] --> E D3[App 3] --> Esubgraph E [通用服务-我们的应用] E1[通用输入适配层
参数配置/规则模板/数据转换] E2[核心业务处理层
保持稳定] endE --> F[通用输出层
接口/事件/页面] F --> G[其他应用] end

架构师的指令:

“我们要做的不是一个只能接一种数据、服务一个应用的‘烟囱’。我们要做的是一个 万能适配器

比如设计‘证书服务’,不要只想着从‘培训系统’接数据。要把‘输入’设计得足够通用:

用‘证书模板’来定义不同类型的证书。

用‘参数配置’来适配不同来源的数据字段。

用‘流程编排’来满足不同客户的不同审核流程。

这样,将来‘荣誉系统’、‘考核系统’想发证书,直接配置一下就能接入, 不需要我们二次开发 。这就是我们工作的价值所在!”

转变思维: 从“实现功能”转变为“设计积木”。每个功能都要先定义清其I-P-O边界。规范流程: 推行“清单驱动开发”,将设计过程文档化、可视化,并将其作为编码的前置条件。明确目标: 以“可配置”、“可复用”为最高追求之一,通过抽象输入、通用化处理,将系统打造成平台型产品,而非项目型定制。质量把关: 用“九性原则”去评审清单和代码,确保性能、安全、可扩展等非功能需求得到满足。

我们将通过四个具体的抓手来确保开发按照“田”字模型进行设计。

将“设计阶段”正式纳入开发流程,并设置为编码的前置关卡。可以使用类似如下的流程图来明确告知团队:

flowchart TD A[需求评审会] --> B{是否新功能/新应用?} B -- 是 --> C[启动-田字模型设计阶段] B -- 否
简单功能/bug修复 --> G[走简化流程或直接开发]subgraph C [设计阶段核心任务] C1[任务1: 撰写] C2[任务2: 撰写] C3[任务3: 撰写] C4[任务4: 产出] endC -- 设计文档完成后 --> D[召开-设计评审会] D -- 评审通过 --> E[架构师在 OA 任务/JIRA 上签字] E --> F[开发团队凭签字
开始编码] D -- 评审不通过 --> C

告诉开发:

“兄弟们,新流程来了。以后所有新需求,在PRD评审后,必须先在JIRA上完成【设计阶段】的任务,产出设计文档并通过评审,这个JIRA任务的状态才能变为【开发中】。这是硬性规定,没有例外。”

创建模板,让开发“填空”,引导他们按“田”字模型思考。这是最重要的落地工具。

模板示例:《XXX 功能/应用架构设计说明书》

说明: 拆解PRD,列出所有主要功能点,并用一句话说清每个功能的输入、处理和输出。

| 场景编号 | 场景名称 | 输入 (Input) | 处理 (Process) | 输出 (Output) | | ---

| C001 | 用户提交订单 | 商品 ID、SKU、数量、用户 ID、地址 | 1.

| ... | ... | ... | ... | ... |

说明: 详细定义每个输入的来源、格式和约束。

| 模型名 | 职责 | 核心属性 | 是否根实体 | | ---

| Order | 订单聚合根,负责订单生命周期 | orderId, userId, totalAmount, status | 是 | | OrderItem | 订单项 | productId, quantity, price | 否 |

说明: 这里的表设计必须源自上面的领域模型和清单。

| 表名 | 存储内容 | 与场景编号关联 | 主要字段 | | ---

| order | 订单主数据 | C001 | id, user_id, amount, status | | order_item | 订单商品数据 | C001 | order_id, product_id, quantity |

| 原则 | 中文释义 | 核心问题 | 设计策略/考量点 | | ---

| Performance | 性能 | 系统响应快慢?吞吐量如何?| 响应时间、吞吐量、资源利用率、缓存、异步、池化、CDN、数据库优化 | | Reliability | 可靠性 | 系统出故障的频率高吗?| 平均无故障时间(MTBF)、错误处理、容错、冗余、自动恢复 | | Availability | 可用性 | 系统在多长时间内可供使用?| 故障时间占比(如99.99%)、冗余、故障转移、负载均衡、优雅降级 | | Security | 安全性 | 系统能否防止恶意攻击和未授权访问?| 认证、授权、审计、加密、防注入、防篡改、漏洞管理 | | Modifiability | 可修改性 | 系统容易修改吗?修改成本高吗?| 模块化、解耦、高内聚低耦合、接口抽象、微服务架构 | | Portability | 可移植性 | 系统能轻易换一个环境运行吗?| 消除对 OS、平台、数据库的依赖,使用容器化技术(Docker) | | Reusability | 可复用性 | 系统的部分功能能轻易被其他项目使用吗?| 组件化、服务化、清晰的接口设计、通用库、设计模式 | | Integrability | 可集成性 | 系统能容易地与其它系统对接吗?| 标准化接口(RESTful API、GraphQL)、通用数据格式(JSON)、提供 SDK | | Testability | 可测试性 | 系统容易测试吗?| 单元测试、依赖注入、mocking、日志、健康检查接口 |

在你的《设计说明书》中,“非功能性设计”部分就应该围绕这九性来展开。以下是如何将它们具体化的示例:

1. 性能 (Performance):指标: 创建订单接口P99 设计: 使用Redis缓存商品信息。对数据库查询建立索引。异步处理非核心逻辑(如发送通知)。2. 可靠性 (Reliability) & 3. 可用性 (Availability):指标: 系统整体可用性达到99.95%(全年故障时间不超过4.4小时)。订单数据零丢失。设计: 数据库采用主从复制。服务部署至少2个实例,并配置负载均衡。关键操作(如扣库存)必须具备幂等性。4. 安全性 (Security):指标: 无高危安全漏洞。设计: 所有接口需通过网关校验JWT令牌。实施RBAC权限模型。对用户输入进行严格校验和过滤,防止SQL注入和XSS攻击。敏感信息(如密码)加密存储。5. 可修改性 (Modifiability) & 6. 可复用性 (Reusability):指标: 新增一种支付方式,开发周期不超过3人/日。设计: 采用策略模式设计支付模块。将订单核心领域与支付、物流等外部能力解耦,定义清晰的接口。力争将“支付能力”提炼为可复用的公共组件。7. 可移植性 (Portability):设计: 使用Docker容器化部署,消除对环境依赖。所有配置外部化,可通过配置中心管理。8. 可集成性 (Integrability):设计: 提供清晰的RESTful API文档(使用Swagger/OpenAPI)。为前端提供便捷的SDK。定义明确的事件契约(如 OrderCreatedEvent ),方便其他系统订阅。9. 可测试性 (Testability):设计: 代码遵循依赖倒置原则,便于注入Mock进行单元测试。提供 /health 端点供健康检查。生成充足的日志,便于集成测试和线上问题排查。

流程和模板有了,就需要一个仪式来审视成果。

会议名称: “架构设计评审会” 或 “技术方案评审会”。参会人: 架构师、该项目的开发人员、主测、感兴趣的其他开发。评审依据: 就是上面要求提交的《架构设计说明书》。评审核心问题:完整性: 三横(I-P-O)是否都覆盖了?清单是否齐全?清晰性: 设计是否清晰?其他人是否能看懂?可复用性(工具化): 输入是否抽象得足够通用?是不是又是“烟囱式”设计?能否通过配置应对未来类似需求?可行性: 技术实现上有无难点?依赖的服务是否可靠?质量: 是否考虑了“九性原则”(如性能、可靠性、可用性、安全性、可修改性、功能性、可变性、互操作性、易用性)?

告诉开发:

“设计文档写完后,预约评审会。在会上,你需要对着你的文档给大家讲清楚你的设计。我们会重点问上面这几个问题。 评审不通过,打回去重新设计,直到通过为止。

量化指标:设计阶段平均耗时: 从PRD评审到设计评审通过的时间。初期会变长,后期应稳定并缩短。返工率: 因设计缺陷导致的代码返工比例。这个指标应该下降。需求吞吐量: 理论上,因为前期设计得好,后期编码调试更顺,整体吞吐量应上升。质性反馈:定期(如每季度)收集开发和产品的反馈:“你觉得新的设计流程有帮助吗?有什么痛点?”收集“因为前期设计好而避免了后期大坑”的成功案例,在团队内部分享,增强大家的认同感。

来源:墨码行者

相关推荐