哪种架构才合适?
合适的架构,往往不是看起来最厉害的那一种。
而是它带来的成本,与你真正要解决的问题相匹配。
不是每个应用都需要微前端、微服务、Event Streaming、Modulith、CQRS、BFF、API Gateway,再加上三个分布式团队和墙上的 Conway 图。
但也不是每个应用都会永远停留在简单的 Todo App 阶段。
架构正是从这里开始。
它首先问的不是:
“现在什么最流行?”
而是那个更让人不舒服的问题:
“我们到底面临什么复杂度?又有哪些复杂度只是因为不安,而被我们自己提前买了进来?”

误区:我们应该先选择一个目标架构
Section titled “误区:我们应该先选择一个目标架构”项目里经常会听到一句话:
“既然要做,就一开始做对。”
听起来很合理。
直到你发现,“做对”往往意味着:
- 照着上一次技术大会看到的方案做;
- 照着大型科技公司的博客做;
- 照着某个工具当前极具说服力的营销方案做;
- 或者按照“如果这个产品已经发展了五年,它大概应该长这样”的想象去做。
问题在于:架构不是愿望清单。
架构是一场下注。
你在押注系统未来会在哪里发生变化。
你在押注哪些部分必须保持独立。
你在押注哪些团队能够承担哪些职责。
你也在押注运维到底能承受多大的复杂度。
而下注就可能押错。
只不过架构押错之后,损失通常不是一笔赌注,而是六个月后出现一个像是把 Enterprise Pattern 全倒进咖啡机搅拌过的系统。
架构解决问题,也会制造新的问题
Section titled “架构解决问题,也会制造新的问题”没有任何一种架构是免费的。
Monolith 并不天然糟糕。
Modulith 也不天然落伍。
微前端并不自动带来灵活性。
微服务也不会自动带来可扩展性。
Multi-Tenant 更不只是加一个 tenantId 字段。
Cross-Platform 也不是“用 CSS 做成响应式就行”。
每种架构都针对某些问题。
同时也会带来新的问题。
这不是缺陷。
这是交易本身。
| 架构决策 | 通常解决的问题 | 通常带来的问题 |
|---|---|---|
| 简单 Monolith | 快速交付、较低运维成本、简单的一致性模型 | 如果缺少边界,耦合会持续增长 |
| 模块化 Monolith / Modulith | 在不引入分布式运维的前提下建立清晰业务边界 | 需要对模块切分和依赖保持长期纪律 |
| 微前端 | 前端团队和部署可以相互独立 | Runtime 复杂度、集成成本、UX 一致性问题 |
| 自包含系统(SCS) | 包含 UI、API 和数据在内的业务自治 | 平台、Observability 和运维成本 |
| 微服务 | 服务独立、独立扩展和技术解耦 | 分布式数据、网络效应、复杂故障模式和运维 |
| Multi-Tenant | 多租户能力、数据隔离和产品变体 | 权限、数据建模、测试和迁移复杂度 |
| Cross-Platform / Multi-UI | 多种 Client 和使用场景 | API 设计、State Model、Design System 和发布协调 |
因此真正的问题不是:
“哪种架构最好?”
而是:
“我们必须解决哪些问题?为了这些问题,我们又有能力承担哪些新问题?”

简单的 Todo App,就应该拥有简单的架构
Section titled “简单的 Todo App,就应该拥有简单的架构”Todo App 在架构讨论里经常被滥用。
一种用法是证明一切都很简单:
“不就是 CRUD 吗?”
另一种则是把它当成游乐场,用来演示微服务、Event Sourcing、CQRS、DDD、Kafka、Kubernetes,再顺手加三层前端架构。
两种都不太合理。
简单的应用值得拥有简单的架构。
这不是轻视问题。
恰恰是尊重问题。
如果一个应用的业务复杂度有限,由一个小团队开发,集成压力不高,不需要复杂的角色模型,而且可以接受统一部署,那么一个结构清晰的 Monolith 通常完全足够。
甚至可能就是最好的方案。
不是因为 Monolith 天生优秀。
而是因为它运维成本低,本地开发简单,不需要把数据一致性拆散到网络边界之外,也不会迫使团队在产品还没有真正存在之前,先花几个月搭一套平台。
糟糕的 Monolith 当然糟糕。
但结构清晰的简单 Monolith 是一种优势。
区别不在名字。
而在边界。
当应用开始增长
Section titled “当应用开始增长”总有一天,“先塞到这里吧”会开始不够用。
这通常不会发生在某个戏剧性的下午。
它是逐渐出现的。
一开始,一个 Feature 被分散在三个 Component 中。
然后一个 Dialog 依赖了某个已经服务于另外五个 Use Case 的 Store。
再后来,Template 开始了解 API 细节。
权限判断出现在谁都想不到的位置。
最后,“编辑客户”这样一个修改,需要同时调整五个在 Jira 看来毫不相关的区域。
欢迎来到日常架构工作。
到了这里,并不意味着你立刻需要一个“更大的架构”。
但你需要一个更有意识的架构。
此时更重要的是:
- 按业务能力切分 Feature;
- 清晰的模块;
- Facade;
- 显式 Boundary;
- 稳定的 ViewModel;
- 职责清楚的 Store;
- 放在正确位置的测试;
- 依赖规则;
- 可追踪的数据流;
- 以及“谁真正负责哪块业务”的问题。
架构从这一刻开始,不再主要像文件夹结构,而更像系统的可变更性。

Modulith:经常被忽视的中间地带
Section titled “Modulith:经常被忽视的中间地带”在“所有东西都放在一个锅里”和“所有东西都跨网络分布”之间,有一块很多项目关注得太少的区域:
Modulith。
Modulith 不是一个换了更漂亮文件夹名的 Monolith。
它的核心是:系统依然可以统一部署,但内部拥有清晰的模块化结构。业务区域有明确边界,依赖受到控制,技术职责和业务职责都能在代码中被看见。
这可以带来非常大的价值。
你能得到很多清晰架构边界的好处,却不必马上买下分布式系统的全部成本。
没有 Service Mesh。
没有分布式事务。
没有为了一个小业务修改而触发十次部署。
也没有穿过五个 Container、三份 Log,最后只看到 Dashboard 写着“某个东西变红了”的 Debugging Safari。
Modulith 尤其适合这些情况:
- Domain 正在增长;
- 一个或多个团队需要明确的业务区域;
- 统一部署仍然可以接受;
- 数据一致性依然重要;
- 运维应该保持轻量;
- 同时又不希望系统内部逐渐变成一团泥。
因此,Modulith 并不是退步。
很多时候,它反而是那些系统在有人喊出“微服务”之前真正需要的架构。
这个主题值得单独写一篇文章。
微前端:听起来很灵活,代价也很真实
Section titled “微前端:听起来很灵活,代价也很真实”微前端听起来非常诱人。
独立团队。
独立部署。
各自的 Release Cycle。
清晰的产品区域。
技术自由。
更少协调。
在真正适合的上下文里,这些价值确实存在。
例如多个团队负责清晰分离的业务区域,不同产品线需要独立演进,部署必须彼此独立,或者某些前端区域在组织上确实需要由不同团队完整负责。
但微前端不会替你修复糟糕的边界。
它只会让糟糕的边界更明显。
以前耦合只是代码里的麻烦。
之后它还会出现在 Runtime、Routing、Shared Dependency、Auth、Styling、Design System、API Contract、Build Pipeline 和 Deployment 中。
恭喜。
同一个问题现在有了更多可以着火的地方。
微前端不是用来修补模糊 Domain 的工具箱。
如果连一个 Feature 属于谁、哪些数据允许共享、哪些 UI State 是共同状态、业务职责究竟如何切分都不清楚,那么 Module Federation 也帮不了你。
这不是架构收益。
只是把不清晰分布了出去。

自包含系统:自治不只是拥有自己的 Build
Section titled “自包含系统:自治不只是拥有自己的 Build”自包含系统(Self-contained System,SCS)再往前走了一步。
一个业务区域不仅拥有前端的一部分,通常还拥有 API、数据、业务逻辑、Deployment 和运维。目标是形成强业务自治。
如果不同业务区域确实需要独立交付、独立修改和独立运行,这种方式非常有价值。
不是架构图上的自治。
而是真实团队、真实责任、真实运维和真实边界。
SCS 比较适合以下场景:
- 业务 Domain 能够清晰分离;
- 团队承担端到端职责;
- Release 必须独立;
- 各业务区域的数据所有权很重要;
- 不同区域存在不同的运维或扩展要求;
- Integration 应该通过明确 Contract 发生。
但如果太早使用,很快就会变成平台马戏团。
团队不再主要开发产品,而是在处理 Shell、Gateway、Auth、Observability、Deployment Matrix、Contract Versioning、本地开发环境,以及“为什么这个区域在 Preview 可以运行,另一个却只在 Marco 的电脑上能运行”之类的问题。
SCS 在组织和 Domain 真正匹配时很强。
如果只是因为一张 PPT 上“自治团队”看起来很漂亮,那它会很痛苦。
Multi-Tenant 不只是一个 tenantId
Section titled “Multi-Tenant 不只是一个 tenantId”还有一个经典说法:
“Multi-Tenant?加个
tenantId就行。”
这是那种会让架构师暂时望向窗外,思考今天是否还有必要继续工作的句子。
当然,tenantId 可以是解决方案的一部分。
但多租户能力很少只是一个字段。
真正的问题包括:
- 数据是否必须严格隔离?
- 每个 Tenant 是否有不同的角色和权限模型?
- 是否存在租户级配置?
- 是否存在不同 Workflow?
- 不同 Design?
- 不同的数据保留要求?
- 不同 Integration?
- 不同 Release Approval?
- 不同监管要求?
- 数据库共享还是隔离?
- Migration 如何执行?
- 如何测试租户特有行为?
一个带角色的 Login 还不是 Multi-Tenant 架构。
不同客户群恰好使用同一份代码,也不意味着已经拥有一套可持续的多租户平台。
当数据隔离、配置、权限、变体和运维已经不能“顺手做掉”时,Multi-Tenant 才真正成为架构驱动力。
这时就必须认真处理。
不必恐慌。
但必须认真。
Cross-Platform 不等于“响应式 UI”
Section titled “Cross-Platform 不等于“响应式 UI””Cross-Platform 也是类似的问题。
一个 Web App 在手机上没有完全崩掉,并不意味着你已经有了 Cross-Platform Strategy。
多个 Client 会改变系统。
Desktop Web 的交互模式和 Mobile App 不一样。
面向消费者的公开 App 与内部 Admin Tool 的安全需求不同。
Kiosk、Native App、Portal 和 Backoffice 也绝不是四种屏幕尺寸而已。
多个 UI 会影响:
- API 设计;
- ViewModel;
- 权限;
- 错误处理;
- Offline 能力;
- Caching;
- Navigation;
- Testing;
- Release Cycle;
- Design System;
- Support;
- Monitoring;
- 产品职责。
当不同 Client 不只是“长得不一样”,而是拥有不同的业务使用模型时,Cross-Platform 才真正成为架构驱动力。
Multi-UI Framework 同样不是目的本身。
Angular、React、Native、Web Components 或任何 Shell 概念,都不会自动回答这些问题:业务语义如何保持一致?哪些 UI 可以共享?哪些变体被允许?团队如何安全发布变化?
技术可以帮助你。
但技术不能替你做决定。

“我们从一开始就做成可扩展”通常意味着“我们从一开始就做得很复杂”
Section titled ““我们从一开始就做成可扩展”通常意味着“我们从一开始就做得很复杂””可扩展性当然重要。
但很多系统并不是因为无法水平扩展而失败。
它们失败,是因为没人再知道一个业务修改到底应该放在哪里。
或者因为团队需要三天才能把本地环境全部启动起来。
或者因为一个小改动要经过五个 Repository、两条 Pipeline、三个 Review 和一轮协调。
或者系统理论上拥有“专业运维”,但实际上没人知道为什么 Preview 环境里的 Login 只有一半时间能用。
“可扩展”不是最大化架构复杂度的通行证。
到底要对什么扩展?
用户负载?
团队数量?
Feature 数量?
Tenant 数量?
Release 频率?
Integration 数量?
监管要求?
十年的维护周期?
这些是完全不同的扩展方向。
它们需要不同的回答。
为团队自治而设计的架构,对一个小团队可能过于沉重。
为用户负载设计的架构,业务上仍然可能是一片泥潭。
为多产品线设计的架构,对一个内部业务应用来说可能贵得荒谬。
没有明确上下文的 Scalability,只是一个听起来更专业的直觉。
架构从来不只是技术决策
Section titled “架构从来不只是技术决策”另一个常见误区是:
“架构是技术决策。”
不是。
架构同时也是团队、产品、运维和管理决策。
当然,技术因素非常重要。
Framework、Runtime、API、Database、Build System、Deployment、Security、Observability —— 都重要。
但架构还决定:
- 哪些团队相互依赖;
- 谁必须批准修改;
- 哪些部分共同承担责任;
- 产品变体可以在哪里出现;
- 新 Feature 可以多快交付;
- 哪些故障能够被隔离;
- 运维成本有多高;
- 新开发者需要多久才能真正参与。
如果 Management 希望通过架构实现团队独立,那这些团队也必须真实存在。
如果公司想做微服务,但实际上只有一个团队顺便负责全部运维,那并不是现代架构。
那只是一个带 Pager 的爱好。
如果产品要卖 Multi-Tenant 能力,就必须把多租户看成产品能力,而不是以后再补的一列数据库字段。
架构无法凭空消除组织问题。
但它可以把这些问题暴露出来。
很多时候,这已经足够让人不舒服了。

一张决策地图
Section titled “一张决策地图”与其按照 Buzzword 选架构,不如先画一张更朴素的地图。
它不完美。
也不是最终答案。
但比“大家现在都这么做”可靠得多。
1. 业务复杂度
Section titled “1. 业务复杂度”系统里究竟有多少真正的业务复杂度?
只是简单的数据维护?
还是包含规则、变体、Workflow、角色、状态转换、计算、审核和例外?
业务复杂度越高,清晰模型、边界、Use Case 和测试就越重要。
但高业务复杂度并不自动意味着微服务。
很多时候,它首先意味着:需要更好的模块化。
2. 变化频率
Section titled “2. 变化频率”哪些部分变化最频繁?
UI Flow 经常变化吗?
业务规则经常变化吗?
Integration 经常变化吗?
不同 Tenant 的需求经常变化吗?
不同产品线需要独立变化吗?
架构应该承载可以预见的变化方向。
而不是提前承载一个想象中的平台帝国。
3. 团队规模与团队自治
Section titled “3. 团队规模与团队自治”真正有多少团队在这个系统上工作?
一个小团队很少需要与五个独立产品团队相同的架构。
如果团队需要独立交付,边界、职责和 Deployment 就必须支持这种独立性。
但如果所有修改最终还是要经过同样的三个人,那么最大化分布往往只会最大化协调。
4. UI 和 Client 的数量
Section titled “4. UI 和 Client 的数量”只有一个 UI?
还是多个?
Admin、Consumer、Backoffice、Mobile、Kiosk、Partner Portal、内部 Tool?
当不同 Client 有不同使用场景、权限、数据需求和 Release Cycle 时,多 Client 是很强的架构驱动力。
这时 BFF、API Boundary、Design System 和共享模型边界才真正值得讨论。
不是因为这些词听起来高级。
而是因为它们可能减少真实摩擦。
5. 多租户能力
Section titled “5. 多租户能力”真的存在 Tenant 吗?
还是只是带角色的用户?
数据是否需要隔离?
Workflow 是否需要变化?
是否必须支持客户级配置?
Feature 或 Release 是否要按 Tenant 控制?
多租户能力越深地影响产品,就越应该越早被架构显式建模。
等到后面“再加个 tenantId”,大概和车开到一半再换车轴一样轻松。
6. 集成压力
Section titled “6. 集成压力”有多少外部系统参与?
它们稳定吗?
还是经常变化?
数据模型是否来自外部?
是否包含 Legacy System?
是否有异步流程?
系统边界上是否需要业务语义转换?
高集成压力意味着你需要清晰 Adapter、Anti-Corruption Layer、稳定接口和良好 Observability。
它不自动意味着最大化分布。
但肯定意味着不能“DTO 一路传到 Template”。
7. Deployment 独立性
Section titled “7. Deployment 独立性”系统的不同部分真的必须独立部署吗?
真的?
不是“感觉会很酷”。
而是:是否存在业务、组织或监管原因,要求区域 A 上线时区域 B 保持完全不变?
如果有,微前端、SCS 或微服务就变得更值得评估。
如果没有,统一 Deployment 往往更简单、更安全,也更便宜。
8. 运维与 Observability 要求
Section titled “8. 运维与 Observability 要求”团队真的能运营这套架构吗?
Logging、Tracing、Metrics、Alert、故障分析、Rollback、数据 Migration、本地开发、Preview Environment、Security Patch。
没有 Observability 的分布式系统并不是现代架构。
它只是制造烟雾。
系统越分布式,运维就越应该从一开始进入架构。
而不是等浏览器第一次只显示一个 504 时再开始补。
9. 应用寿命
Section titled “9. 应用寿命”这是一个短期 Tool?
MVP?
内部表单?
还是要运行十年的平台?
长期系统需要对结构、测试、模块化、文档和 Ownership 做不同程度的投资。
但同样要注意:长期并不能自动合理化任何复杂度。
你可以让架构具有演进能力,而不必第一天就模拟它的最终形态。
10. 监管、权限与数据隔离
Section titled “10. 监管、权限与数据隔离”是否有较高的数据保护、审计、权限或数据隔离要求?
如果有,架构就不只是代码质量问题。
它还涉及信任、可追溯性、访问控制和故障隔离。
这会影响数据模型、API、Logging、测试、角色模型、Tenant Boundary 和 Deployment Strategy。
没错,这比“Security 后面再补”麻烦得多。
11. Overengineering 风险
Section titled “11. Overengineering 风险”最后一个问题最让人不舒服:
我们今天究竟能承受多大的架构复杂度?
不是理论上。
是今天。
用这个团队。
这些技能。
这个预算。
这种运维成熟度。
这样的产品不确定性。
这样的时间压力。
Overengineering 在一开始很容易显得专业。
后来,它会变成每次修改前都必须经过的收费站。
一个粗略方向
Section titled “一个粗略方向”下面这张表不是规则。
它只是一张地图。
而任何地图都不能替代你抬头看真实环境。
| 上下文 | 经常值得考虑的方向 |
|---|---|
| 小型 App、小团队、业务复杂度有限 | 简单、结构清晰的 Monolith |
| 业务复杂度增长,一个或少数团队,可以接受统一 Deployment | 模块化 Monolith / Modulith |
| 多个业务区域、清晰 Team Ownership、需要独立 UI Release | 评估微前端 |
| 业务区域需要从 UI、API、数据到运维的端到端自治 | 评估自包含系统(SCS) |
| Service 必须独立扩展、部署或拥有独立业务数据 | 评估微服务 |
| 多个 Tenant,需要隔离、配置、变体和独立权限 | 显式设计 Multi-Tenant 架构 |
| 多个 Client,且使用场景不同 | 评估 Cross-Platform Strategy、BFF、Design System |
| 多团队,需要统一 UI 语言和重复 Pattern | Design System / UI Platform 可能有价值 |
| 高外部系统集成压力 | Adapter、ACL、稳定 API Boundary、Observability |
最重要的词是“评估”。
不是“采用”。
架构决策不是在线下单。
更好的做法:按照系统承受的压力选择架构
Section titled “更好的做法:按照系统承受的压力选择架构”一个更好的架构决策,不应该从下面这句话开始:
“我们要不要微前端?”
而应该从这些问题开始:
- 什么必须能够独立修改?
- 什么必须能够独立部署?
- 什么必须在业务上保持分离?
- 什么反而必须保持在一起?
- 哪些数据真正属于同一个业务范围?
- 哪些部分变化最频繁?
- 哪些团队拥有哪些区域?
- 哪些 UI 部分必须保持一致?
- 哪些变体是真正的产品能力,哪些只是 Sonderfall?
- 我们今天能够运营多大的复杂度?
- 我们真正需要多大的复杂度?
- 如果判断错了,会发生什么?
- 能否让系统未来继续成长,而不是今天就把未来全部预建出来?
这些问题没有带着很多方框和箭头的架构图那么华丽。
但它们能避免一种很常见的情况:最后的架构更多反映了团队自己的愿望,而不是产品真正的需要。

后续文章会继续展开的内容
Section titled “后续文章会继续展开的内容”这篇文章有意保持在总览层面。
下面这些架构形式都值得单独讨论,因为它们各自拥有不同优势、陷阱和决策问题。
计划中的专题包括:
-
Monolith
什么时候统一系统是一种优势,什么时候它开始失控。 -
Modulith
如何在不引入分布式运维的情况下建立清晰业务边界。 -
微前端
什么时候独立前端真正有价值,什么时候只是把混乱分布出去。 -
自包含系统(SCS)
端到端 Ownership 到底意味着什么,以及它需要怎样的成熟度。 -
微服务
为什么 Service Boundary 更多取决于数据、Ownership 和运维,而不是 Repository 数量。 -
Multi-Tenant 架构
为什么多租户是一项产品能力,而不仅仅是数据库字段。 -
Cross-Platform Frontend
多个 Client 对 API、ViewModel、Design System 和 Release 真正意味着什么。 -
BFF / API Gateway
什么时候面向 Client 的 API 能减少摩擦,什么时候它只增加了一层转发。 -
Design System / UI Platform
什么时候共享 UI 结构能够加速团队,什么时候它会成为中心瓶颈。
好的架构不是尽可能大。
它应该大到足以承载问题,又小到仍然能够被人理解。
它承载可以预见的变化方向。
它与团队结构匹配。
它尊重真实的运维条件。
它分开必须分开的东西。
也让本该在一起的东西继续在一起。
并且,它有勇气不比实际需要更“ impressive”。
好的架构不是尽可能大。好的架构应该大到足以承载问题,又小到仍然能够被理解。