Ch 4 平台五层模型与设计哲学¶
面包屑
本书主页 › Part II 架构设计 › Ch 4
项目第 0 年 · 架构设计期——五层模型诞生
本章你将学到¶
- 平台五层模型的完整定义与各层职责
- 为什么是五层而非三层——共享、模式、业务、CI 的治理分工
- 分层思维背后的"平台工程"理念与 Well-Architected 视角
如果说整个平台是一座建筑,那五层模型就是它的地基和承重墙。这个模型不是第一天画好的——它是在第 0 年反复推演和争论中慢慢长出来的。
一开始我画了个三层模型(Generic Modules → Business IaC → CI/CD),觉得够了。但 Aurora 平台架构组负责人提了一个尖锐的问题:"共享资源谁管?S3 数据湖桶、Redshift 集群、IAM 基座——每个业务仓自己建的话,命名冲突怎么办?权限不一致怎么办?"这个问题让我意识到三层兜不住,于是拆出了 Core Infra 层。后来 CI/CD 设计阶段又发现 Actions 和 Workflows 混在一起复用不了,于是又拆出了 Custom Actions 层。最后就成了五层。
这个过程本身就说明了一件事:好的分层不是一次设计出来的,是在"发现痛点→加一层抽象→验证"的循环里逼出来的。
4.1 五层模型:Core Infra → Generic Modules → Business IaC → Custom Actions → Reusable Workflows¶
平台的基础设施不是一锅粥,而是五层分层架构。每层有明确的职责边界,上层依赖下层,下层不知道上层存在。
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart TB
subgraph L5["第五层:Reusable Workflows(GitHub Actions 可复用工作流)"]
wf_tf@{ icon: "devicon:githubactions", form: "rounded", label: "Terraform CI", pos: "b", h: 36 }
wf_glue@{ icon: "devicon:githubactions", form: "rounded", label: "Glue CI", pos: "b", h: 36 }
wf_lambda@{ icon: "devicon:githubactions", form: "rounded", label: "Lambda CI", pos: "b", h: 36 }
wf_cfg@{ icon: "devicon:githubactions", form: "rounded", label: "Config CI", pos: "b", h: 36 }
end
subgraph L4["第四层:Custom Actions(被 Workflows 调用)"]
act_chg[变更检测]
act_cred[凭证获取<br/>OIDC + AWS STS]
act_pkg[打包]
act_deploy[部署<br/>含失败自动回滚]
end
subgraph L3["第三层:Business IaC(业务域 Terraform 仓库,6 个)"]
biz_a[domain-a<br/>SFE 销售效能域]
biz_b[domain-b<br/>MA 市场准入域]
biz_c[domain-c~f<br/>零售/患者/主数据等 4 域]
end
subgraph L2["第二层:Generic Modules(可复用 Terraform 模块库)"]
mod_s3@{ icon: "devicon:terraform", form: "rounded", label: "S3 Module", pos: "b", h: 36 }
mod_glue@{ icon: "devicon:terraform", form: "rounded", label: "Glue Module", pos: "b", h: 36 }
mod_lambda@{ icon: "devicon:terraform", form: "rounded", label: "Lambda Module", pos: "b", h: 36 }
mod_sf@{ icon: "devicon:terraform", form: "rounded", label: "SF Module", pos: "b", h: 36 }
mod_ddb@{ icon: "devicon:terraform", form: "rounded", label: "DDB Module", pos: "b", h: 36 }
end
subgraph L1["第一层:Core Infra(全平台共享基础设施)"]
core_s3@{ icon: "logos:aws-s3", form: "rounded", label: "S3 数据湖桶", pos: "b", h: 36 }
core_rs@{ icon: "logos:aws-redshift", form: "rounded", label: "Redshift 集群", pos: "b", h: 36 }
core_iam@{ icon: "logos:aws-iam", form: "rounded", label: "IAM 基座", pos: "b", h: 36 }
core_sm@{ icon: "logos:aws-secrets-manager", form: "rounded", label: "Secrets Manager", pos: "b", h: 36 }
core_vpc@{ icon: "logos:aws", form: "rounded", label: "VPC 网络", pos: "b", h: 36 }
end
wf_tf --> act_cred
wf_tf --> act_deploy
wf_glue --> act_pkg
wf_lambda --> act_pkg
wf_cfg --> act_chg
act_deploy --> biz_a
act_deploy --> biz_b
act_deploy --> biz_c
biz_a --> mod_s3
biz_a --> mod_glue
biz_a --> mod_lambda
biz_a --> mod_sf
biz_a --> mod_ddb
biz_a -.->|remote_state| core_s3
biz_a -.->|remote_state| core_rs
biz_a -.->|remote_state| core_iam
biz_a -.->|remote_state| core_vpc
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpInfo fill:#f6f2ff,stroke:#8a3ffc,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
class wf_tf,wf_glue,wf_lambda,wf_cfg bpInfo
class act_chg,act_cred,act_pkg,act_deploy bpProcess
class biz_a,biz_b,biz_c bpDecision
class mod_s3,mod_glue,mod_lambda,mod_sf,mod_ddb bpSuccess
class core_s3,core_rs,core_iam,core_sm,core_vpc bpData
linkStyle default stroke:#697077,stroke-width:2px
linkStyle 8,9,10,11 stroke:#007d79,stroke-width:2px,stroke-dasharray:5 4
图 4-1 五层模型:Core Infra → Generic Modules...
各层职责¶
| 层 | 仓库 | 职责 | 谁来维护 |
|---|---|---|---|
| Core Infra | core-infra |
共享基础资源:数据湖 S3 桶、Redshift 集群、IAM 基座、Secrets、VPC | 平台架构组 |
| Generic Modules | generic-modules |
通用 Terraform 模块库,封装 AWS 资源的标准创建方式 | 平台架构组 |
| Business IaC | business-domain-{a..f} |
各业务域的 IaC,引用 generic modules 组装自己的资源 | 业务域团队 |
| Custom Actions | ci-actions |
可复用的 GitHub Actions 组件(变更检测、凭证获取、打包) | 平台 CI/CD 组 |
| Reusable Workflows | ci-workflows |
可复用的 CI/CD 工作流(Terraform CI、Glue CI 等) | 平台 CI/CD 组 |
表 4-1 各层职责
这张表里"谁来维护"一栏是我和 Aurora 平台架构组吵得最久的部分。初版我写的是"平台团队维护所有层",架构组负责人当场否决:"那平台团队就成瓶颈了——每个业务域改个配置都得排队等我们。"这句话点醒了我:分层不光是技术分层,更是组织分工的镜像(Conway 定律)。于是我把 L3(Business IaC)的维护权交给了业务域团队——他们最懂自己的业务,该让它们自主演进;平台团队只管 L1/L2/L4/L5 这四层"公共底座"。这个分工后来被证明是对的:第二年业务域从 3 个涨到 6 个的时候,平台团队没被业务需求淹没,因为 L3 的变更不经过平台团队。
表里还有一个容易漏掉的设计——L1(Core Infra)和 L2(Generic Modules)虽然都是平台架构组维护,但职责边界很微妙。L1 管的是"具体的共享资源实例"——就这个 S3 桶、这个 Redshift 集群。L2 管的是"创建资源的标准方式"——创建任意的 S3 桶的通用模块。这个区分就是"实例与类型"的分离:L2 是类型(Class),L1 是全局唯一的实例(Instance)。混在一起的话,改 L2 模块直接冲击 L1 资源,风险失控。分开以后,L2 模块能独立迭代、独立测试,L1 只在版本升级时才引用新版——类型稳了,实例才稳。
层间依赖规则¶
核心原则:依赖只能向下,不能向上。
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart TD
L3@{ icon: "devicon:terraform", form: "rounded", label: "Business IaC", pos: "b", h: 40 } -->|引用模块| L2@{ icon: "codicon:package", form: "rounded", label: "Generic Modules", pos: "b", h: 40 }
L3 -->|引用 remote state| L1@{ icon: "logos:aws", form: "rounded", label: "Core Infra", pos: "b", h: 40 }
L3 -->|调用工作流| L5@{ icon: "devicon:githubactions", form: "rounded", label: "Reusable Workflows", pos: "b", h: 40 }
L5 -->|调用 actions| L4@{ icon: "devicon:githubactions", form: "rounded", label: "Custom Actions", pos: "b", h: 40 }
L1 -.->|不能反向依赖| L3
L2 -.->|不能反向依赖| L3
L4 -.->|不能反向依赖| L5
classDef bpProcess fill:#edf5ff,stroke:#0f62fe,stroke-width:2px,color:#161616
classDef bpData fill:#d9fbfb,stroke:#007d79,stroke-width:2px,color:#161616
classDef bpDecision fill:#fcf4d6,stroke:#f1c21b,stroke-width:2px,color:#161616
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
class L3 bpDecision
class L2,L5,L4 bpProcess
class L1 bpData
linkStyle default stroke:#697077,stroke-width:2px
linkStyle 0,1,2,3 stroke:#198038,stroke-width:2px
linkStyle 4,5,6 stroke:#da1e28,stroke-width:2px,stroke-dasharray:5
图 4-2 层间依赖规则
- 第三层(Business IaC)可以引用第二层(Generic Modules)和第一层(Core Infra 的 remote state)
- 第五层(Reusable Workflows)可以调用第四层(Custom Actions)
- 反过来不行:Core Infra 不能依赖 Business IaC,Generic Modules 不能引用具体业务
这条规则保证:底层变更影响面可控,上层变更不伤及底层。
这条"依赖只能向下"的铁律,是我从企业征信项目的血泪教训里提炼出来的。当时没定这条规则,结果出现了"反向依赖"——核心基础设施仓库图省事,直接引用了某个业务仓的配置变量。短期确实方便了,后果是:改业务仓配置会触发核心基础设施重建,一次"改个参数"变成了"全平台 plan/apply",风险和时间都失控。更糟的是半年后那个业务仓要下线,核心基础设施却"依赖"着它,根本删不掉——僵尸依赖。到 Aurora 我把这条铁律贴在架构评审室墙上,每次 PR review 第一件事就是查依赖方向——宁可一开始麻烦,别后面还债。
这条规则还有一个深层好处:底层可以独立演进。L2 的 Generic Modules 从 v1 升级到 v2,L3 的业务仓可以逐个迁移,不用"一升全升"——因为 L2 不知道 L3 存在,L2 的变更不会强制触发 L3。这种"底层向上兼容、上层按需迁移"的模式,让我们在四年里多次升级模块没引发全局故障。如果依赖是双向的,哪一层变了都是全链路变更,平台根本演进不了(M2 关注点分离的实践价值)。
4.2 为什么是五层而非三层:共享/模式/业务/CI 的治理分工¶
最简化的 IaC 架构是三层:Generic Modules → Business IaC → CI/CD。为什么我们要拆成五层?
三层方案的问题¶
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#edf5ff','primaryTextColor':'#161616','primaryBorderColor':'#0f62fe','lineColor':'#697077','secondaryColor':'#d9fbfb','tertiaryColor':'#f2f4f8','fontSize':'14px'}}}%%
flowchart TB
subgraph 三层方案的问题["三层方案的问题"]
direction TB
P1@{ icon: "codicon:warning", form: "rounded", label: "共享资源(S3/Redshift/IAM)没有治理层<br/>每个业务仓各自创建,导致重复与不一致", pos: "b", h: 40 }
P2@{ icon: "codicon:warning", form: "rounded", label: "CI/CD 混在一起<br/>Actions 和 Workflows 没有分层,复用困难", pos: "b", h: 40 }
end
P1 -- 需要 --> L1_CORE@{ icon: "logos:aws", form: "rounded", label: "独立的 Core Infra 层<br/>统一管理共享资源", pos: "b", h: 40 }
P2 -- 需要 --> L4L5_CI@{ icon: "devicon:githubactions", form: "rounded", label: "拆分为 Custom Actions +<br/>Reusable Workflows 两层", pos: "b", h: 40 }
classDef bpError fill:#fff1f1,stroke:#da1e28,stroke-width:2px,color:#161616
classDef bpSuccess fill:#defbe6,stroke:#198038,stroke-width:2px,color:#161616
class P1,P2 bpError
class L1_CORE,L4L5_CI bpSuccess
linkStyle default stroke:#697077,stroke-width:2px
图 4-3 三层方案的问题
如果只有三层: - 共享资源谁管? S3 数据湖桶、Redshift 集群、IAM 基座这些全局共享资源,放在每个业务仓里各自创建,会重复定义、命名冲突、权限不一致。需要一个独立的 Core Infra 层统一管理。 - CI/CD 怎么复用? GitHub Actions 的 custom actions 和 reusable workflows 是两个不同抽象层次。actions 是"原子操作"(获取 AWS 凭证),workflows 是"流程编排"(Terraform 验证→计划→部署)。混在一个仓库里职责模糊。需要拆成 Custom Actions 层和 Reusable Workflows 层。
这俩问题不是理论推演,是我在三层方案上真实摔过的跟头。第一个(共享资源混乱)出在项目第二个月:domain-a 和 domain-b 各自用 Terraform 建了 S3 数据湖桶,命名分别是 aurora-datalake-a 和 aurora-datalake-b——看起来没冲突,但 Redshift 的 COPY 权限要对两个桶分别授权,IAM role 要维护两套,跨域查询还得做跨桶授权。到第三个业务域接入时,权限矩阵已经一团乱。我不得不停下所有业务开发,花了一周把三个桶合并成 core-infra 统管的单桶——这周重构的痛让我把"共享资源必须独立成层"写进了架构铁律。
第二个(CI/CD 混层)出在第三个月:ci-workflows 仓库里既有 custom actions 又有 reusable workflows,新人根本分不清"该改哪个"。更糟的是,一个 action 被多个 workflow 引用,改 action 得同时验证所有引用它的 workflow——耦合太死。拆成 ci-actions(原子操作)和 ci-workflows(流程编排)两层后,action 独立版本化、独立测试,workflow 只在需要时引用新版本——原子操作和流程编排是不同的变更节奏,不分层就耦合。这个教训在 Ch 27 CI/CD 可复用工作流平台 有详细展开。
五层的治理分工¶
| 治理诉求 | 由哪一层解决 | 举例 |
|---|---|---|
| 共享资源的统一管理 | Core Infra(L1) | 数据湖桶只有一个定义,所有业务仓通过 remote state 引用 |
| 资源创建方式的标准化 | Generic Modules(L2) | 创建 S3 桶的标准方式封装成模块,所有业务仓复用 |
| 业务域的独立演进 | Business IaC(L3) | domain-a 加新表不影响 domain-b |
| CI 原子操作的复用 | Custom Actions(L4) | "获取 AWS 凭证"这个操作只写一次,所有 workflow 复用 |
| CI 流程的标准化 | Reusable Workflows(L5) | "Terraform CI"流程只定义一次,所有业务仓调用 |
表 4-2 五层的治理分工
这张表里每条治理诉求都对应一个真实的"痛点→拆层"故事。"共享资源统一管理"那一行,痛点是第二个月的三桶权限混乱(上节说了);"资源创建方式标准化",痛点是第三个月三个业务仓各自写 S3 模块,80% 代码重复但 20% 各自魔改,一个 bug 要改三处——抽成 Generic Modules 后只改一处;"CI 原子操作复用",痛点是"获取 AWS 凭证"这段逻辑在五个 workflow 里复制了五份,OIDC 升级时要改五处——抽成 Custom Actions 后只改一处。
我想强调的不是"五层合理"这个结论,而是它背后的原则:架构分层的驱动力是实践中的痛点,不是理论上的整洁。没痛点就分层是过度设计,有痛点不拆是欠设计。Aurora 这五层是在"痛点→拆层→验证→新痛点→再拆层"的循环里长出来的,这也是为什么我在引言里说"好的分层不是一次性设计出来的"。如果第一天就硬拆五层,反而会因为没痛过而拆错边界——有些该合的被强拆,有些该拆的被漏掉。
Trade-off
五层的代价是认知复杂度。新人得先搞清楚五层之间的关系才能上手——我第二年统计过,新人平均 2-3 周才能在 L3 独立写业务 IaC,三层方案可能一周就够了。但这是一次性学习成本——一旦理解,所有操作都有规律可循。相反,三层方案上手快,但随着业务域增多,重复和不一致会指数级增长,长期维护成本远超五层。学习成本是线性的,维护成本是指数的——规模上去以后一定选五层。
4.3 分层思维引申:Well-Architected 视角与"平台工程"理念¶
AWS Well-Architected 视角¶
五层模型与 AWS Well-Architected Framework 的六大支柱有对应关系:
| Well-Architected 支柱 | 在五层模型中的体现 |
|---|---|
| 安全性 | L1 统一 IAM 基座、L2 模块内置安全最佳实践(加密/最小权限) |
| 可靠性 | L1 共享资源的高可用配置、L5 CI 流程的验证门禁 |
| 性能效率 | L2 模块封装性能优化(如 Redshift 分布键选择) |
| 成本优化 | L3 业务仓独立管理资源,可按域优化 |
| 卓越运营 | L4/L5 标准化 CI/CD,降低运维负担 |
| 可持续性 | L2 模块统一资源规格,避免过度配置 |
表 4-3 AWS Well-Architected 视角
坦率说,这张映射表不是我在设计五层时主动对照 Well-Architected 画出来的——是平台上线一年后,Aurora 全球总部的架构审计要求提交 Well-Architected 自评报告,我才"事后"做对照。结果让我松了口气:五层模型六个支柱都覆盖了,没明显盲区。但诚实复盘的话,有一个支柱在设计时是被忽视的——可持续性。第一年我只盯"性能"和"成本",资源规格按"够用就好"定,没想过过度配置的浪费。到第二年做 FinOps 成本治理才发现 L2 模块里好几个 S3 桶没配生命周期策略,冷数据一直占着标准存储的费用——事后才补上。
借这个对照我想说的是:Well-Architected 不是设计时的检查清单,而是持续对照的标尺。五层模型的设计驱动力是"解决实践痛点",不是"对照 Well-Architected 打勾"——但事后对照能发现设计时的盲区(比如可持续性)。所以我的建议是:设计时别被框架绑架,上线后定期用框架"查漏补缺"。这比"一开始就照框架设计"务实——没痛过的设计容易拆错边界,而框架对照能帮你找到"哪里该痛但还没痛到"。
"平台工程"理念¶
五层模型体现的是平台工程(Platform Engineering) 的核心思想:
平台工程是"为软件开发团队提供自助式内部开发者平台"的学科。平台团队构建抽象层,让业务团队专注于业务逻辑而非基础设施。
在本书的语境下:
C4Context
title 平台工程团队拓扑 — System Context
Person(platform_team, "平台团队", "维护 L1/L2/L4/L5 四层基础设施,构建抽象层并提供自助式开发者平台")
Enterprise_Boundary(cdp, "CDP 平台") {
System(cdp_platform, "CDP 平台", "提供数据摄取/加工/入仓/激活的全部标准化能力,封装为自助式 API")
}
Person(biz_a, "domain-a 团队", "销售效能(SFE)数据域——在 L3 写 Terraform 业务配置")
Person(biz_b, "domain-b 团队", "市场准入(MA)数据域——独立演进,与 domain-a 隔离")
Person(biz_c, "domain-c~f 团队", "零售/患者/主数据等 4 个数据域——每个域一套独立 IaC 仓库")
Rel(platform_team, cdp_platform, "构建与维护:Modules、Actions、共享资源", "Terraform + GitHub Actions + IAM")
Rel(cdp_platform, biz_a, "赋能:remote_state + tf_module 自助引用", "Terraform remote_state + module")
Rel(cdp_platform, biz_b, "赋能:同上——多域复用、CI/CD 标准化", "Terraform remote_state + module")
Rel(cdp_platform, biz_c, "赋能:新增域只建 L3 仓库即继承全部能力", "Terraform remote_state + module")
UpdateElementStyle(platform_team, $bgColor="#f2f4f8", $fontColor="#161616", $borderColor="#697077")
UpdateElementStyle(cdp_platform, $bgColor="#edf5ff", $fontColor="#161616", $borderColor="#0f62fe")
UpdateElementStyle(biz_a, $bgColor="#f6f2ff", $fontColor="#161616", $borderColor="#8a3ffc")
UpdateElementStyle(biz_b, $bgColor="#f6f2ff", $fontColor="#161616", $borderColor="#8a3ffc")
UpdateElementStyle(biz_c, $bgColor="#f6f2ff", $fontColor="#161616", $borderColor="#8a3ffc")
UpdateRelStyle(platform_team, cdp_platform, $textColor="#0f62fe", $lineColor="#0f62fe")
UpdateRelStyle(cdp_platform, biz_a, $textColor="#8a3ffc", $lineColor="#8a3ffc")
UpdateRelStyle(cdp_platform, biz_b, $textColor="#8a3ffc", $lineColor="#8a3ffc")
UpdateRelStyle(cdp_platform, biz_c, $textColor="#8a3ffc", $lineColor="#8a3ffc")
UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1")
图 4-4 "平台工程"理念
平台团队维护四层基础设施(L1/L2/L4/L5),业务团队只需在 L3 写自己的业务配置。这就像 Kubernetes 提供了平台,应用团队只需要写 Deployment YAML——好的平台让消费者感觉不到底层的复杂性。
这个理念在 Aurora 落地的关键决策是:L3 业务团队写 Terraform 时,不需要懂 Terraform 的全部。我让平台团队在 L2 的 Generic Modules 里做了"意图级封装"——业务团队只需要声明"我要一个 S3 桶存 SFE 原始数据",模块内部自动处理加密、生命周期、权限策略、命名规范。业务团队感知到的是"填几个参数",而不是"写 200 行 HCL"。这就是平台工程的核心:把复杂性沉到平台层,把简洁性浮给业务层。
这个决策第一年有争议——有人说"封装太多会让业务团队失去灵活性"。我的反驳是:灵活性是给平台团队的,不是给业务团队的——业务团队要的是"快速建资源",不是"灵活配置每个参数"。如果业务团队真需要某个模块没暴露的参数,走 PR 给 L2 模块加一个 variable——这比每个业务仓各自魔改安全得多。到第二年,6 个业务域团队都在 L3 独立交付,平均一个新业务域接入只需 1-2 周——这个速度在三层方案下是不可想象的(三层时要平台团队全程参与)。平台工程的成功标准不是平台有多强,而是业务团队多快能自助交付——这是我在 Aurora 最重要的架构成就之一(M7 平台工程)。
引申
如果你想深入理解平台工程,推荐阅读《Team Topologies》(Matthew Skelton)——书中提出了"Stream-Aligned Team"和"Platform Team"的团队拓扑模型,与本书的五层架构高度契合。平台团队是"赋能者"而非"控制者",它的成功标准是业务团队的交付速度。我读这本书时有种"相见恨晚"的感觉——书里用理论完美解释了我用直觉做出的那些决策。如果项目第一天就读过,很多弯路能省掉。
本章小结¶
- 平台采用五层分层架构:Core Infra → Generic Modules → Business IaC → Custom Actions → Reusable Workflows
- 每层职责明确,依赖只能向下不能向上,保证变更影响面可控
- 五层而非三层的原因:共享资源需独立治理层(L1),CI/CD 需分原子操作与流程编排两层(L4/L5)
- 五层模型体现 AWS Well-Architected 六支柱与平台工程理念:平台团队构建抽象,业务团队专注业务
下一章
Ch 5 端到端数据流全景 —— 理解了"分层怎么分",接下来看"数据怎么流":一条数据从上游到消费的完整旅程。