跳转到内容

微服务根本行不通?

“微服务根本行不通。”

听到这种话,做架构的人大概会先闭一下眼睛,深呼吸,然后希望后面还能跟着半句解释。

因为这句话甚至可能是对的。

只是大概率不是说话的人以为的那个意思。

真正值得问的,并不是“微服务到底行不行”,而是:

究竟是什么没有起作用?

那真的是微服务吗?
还是一个分布式单体?
划分的是业务边界吗?
还是只是技术 Artifact?
团队真的可以独立交付吗?
还是一次 Release 依然要协调五个 Service、三个团队和一个 Change Board?

从前端角度,还要再追问一个特别重要的问题:

UI 是业务边界的一部分,还是所有糟糕边界最后重新粘在一起的地方?

并不是每个分布式应用都是微服务架构。

如果一个人做了 15 年软件之后说:“微服务不行”,不应该傲慢地把这句话一笑置之。

背后很可能有真实经验。

可能经历过昂贵的项目、拖沓的 Release、不稳定的环境、模糊的职责、难以测试的流程、跨七个系统的 Debugging,以及只因为架构太复杂才不得不开的会议。

这些痛苦都是真的。

但它们并不能自动证明“微服务不工作”。

它们首先只能证明一件事:

这种具体的分布式架构没有工作好。

从这里开始,事情才真正有意思。

因为很多被包装成“微服务”的系统,在业务意义上根本不是微服务架构。它们只是采用了分布式技术,却仍然用中心化方式思考。

更短一点说:

把一个 Monolith 通过 HTTP 分散出去,并不会自动得到清晰的业务架构。

当有人说“微服务不工作”时,必须继续追问。

不是为了抬杠,而是因为否则这个诊断毫无价值。

“不能独立部署”没有起作用?
那它们真的独立部署过吗?

“团队无法自治”没有起作用?
那真的存在 Team Ownership 吗?

“Service 应该按业务边界切分”没有起作用?
还是实际上只拆了 Table、技术 Module 或 CRUD 区域?

“数据归属应该清晰”没有起作用?
还是多个 Service 同时访问相同的数据、相同的 Model,甚至同一个所谓的事实来源?

“系统应该可观察”没有起作用?
还是一个错误跨越 Service Boundary 之后,追踪难度和没有运单号的包裹差不多?

“UI 应该因此更简单”没有起作用?
还是 Frontend 必须根据五个 API、三个 Status Code 和两个历史特殊情况,自己猜出用户现在到底允许看到什么?

这些不是同一个问题。

它们的原因也不同。

把所有东西都概括成“微服务不工作”,就太省事了。大概相当于冬天开着夏胎在结冰路面上打滑,然后得出结论:“汽车不工作。”

不能说完全错。

但对解决问题帮助不大。

不是每个分布式系统都是微服务架构

Section titled “不是每个分布式系统都是微服务架构”

微服务架构并不等于:

  • 很多 Repository
  • 很多 Deployment
  • 很多 REST API
  • 很多 Docker Container
  • 很多团队和很多 Jira Board

这些首先只是“分布式”。

架构不是由数量产生的。

架构来自边界、职责和可变更性。

一个 Service 只有在真正封装了业务职责、拥有清晰的数据归属、可以被合理观察、能够独立测试,并且由一个团队以无需每次改动都跨半个公司的方式负责时,才真正带来价值。

如果 Service A 的改动几乎总意味着 Service B、C、D 也要跟着改,就谈不上真正独立。

如果 Release 必须统一协调,Deployment Boundary 就只是装饰。

如果多个 Service 共用一个 Domain Model,它们往往也一起共享这个 Model 带来的痛苦。

如果一个 UI Flow 为了正确启用一个 Button,需要同步调用五个 Service,那么业务边界可能根本不在架构图认为的地方。

分布式单体,本质上就是一个学会制造 Network Error 的单体。

听起来有点刻薄。

但常常非常准确。

这里有一个非常老的架构图,也隐藏着最常见的误区之一:

最下面是数据库。
上面是 Service。
再上面是 API。
最上面是 UI。

好像 UI 只是一层薄薄的油漆。负责一点外观,一点 Form、一点 Table、几个 Button。

过去这种理解已经值得怀疑。

今天更是错误。

现代 Frontend 是交互式、异步、由 State 驱动的应用。它们包含用户 Flow、权限、Validation、Error State、中间步骤、Optimistic Update、ViewModel、业务决策,以及大量真正影响产品含义的内容。

UI 不是盖在系统最上面的东西。

它本身就是业务切分的一部分。

用户不会体验“Layer”。

他们体验的是 Flow。

他们想提交一个诉求、检查一笔订单、完成一项任务、理解一张账单、做出一个决定,或者解决一个错误。

没有用户会想:

“太好了,我现在正处于 Service B 与 Status C 聚合之间的 API Layer。”

也许架构师除外。

不过做产品设计的时候,架构师最好还是在有人监督的情况下接近真实用户。

用户体验的不是 Layer,而是业务 Flow。

SOA 的遗产:技术集成不等于产品 Flow

Section titled “SOA 的遗产:技术集成不等于产品 Flow”

SOA 并不愚蠢。

这点应该公平地说。

SOA 有很多正确的想法:

  • Service
  • Contract
  • Loose Coupling
  • Integration
  • Reuse
  • 清晰接口

问题不在于“用 Service 组织系统”这个想法本身。

问题在于,很多时候人们把技术 Integration 当成了业务 Architecture。

许多 SOA Landscape 强烈依赖中心模型、Integration Flow、ESB、Orchestration 和 Governance。这样做有历史原因。大型企业系统并不简单,Legacy System 也不会因为架构师画了一张新图就消失。总得有人负责 Integration、Translation、Stabilization 和 Protection。

但从产品与前端角度看,这很容易制造一个核心冲突。

很多 SOA 思维大致是:

  • Model 由中心技术层建模
  • Service 提供数据
  • UI 放在最上面
  • 使用场景随后再考虑

可用户并不是和 Canonical Data Model 打交道。

他们面对的是 Intent、State、Decision、Error、Permission、中间步骤和具体含义。

一个 Canonical Model 可以在技术上非常一致,却仍然不适合某个具体 UI 场景。

Canonical Model 并不自动等于 Product Model。

Technical Model 解释系统如何交换数据。
Product Model 解释这些数据对用户意味着什么。

这是巨大的差别。

如果忽略这个差别,UI 迟早会变成“架构翻译办公室”:后端从来没有真正建模用户 Flow,最后全部由前端重新解释。

技术 Integration 并不等于业务 Architecture。

很多 SOA Landscape 还有另一个问题:核心知识只存在于极少数人那里。

ESB Specialist。 Integration Architect。 真正理解 Canonical Model 的人。 知道哪种情况该以哪个 Service 为主的人。 熟悉历史 Transformation 的人。 记得某个客户对应哪个特殊例外的人。

只要这些人还在,系统就还能运转。

也许不漂亮,也不快。

但至少能跑。

一旦这些人不在,Architecture 就会变成 Archaeology。

大家开始挖掘:

为什么这里要 Rename 这个字段?
为什么 Status ACTIVE 在这个 Flow 里不能表示 active?
为什么流程先调用 Service A,再调用 B,然后又回 A?
为什么这个 Transformation 存在两份?
为什么事实存在 System X 里,但只对 Tenant Y 成立?
为什么除了 Ralf 之外没人知道?

这不是 Ralf 的个人失败。

Ralf 很可能多年都在帮这个系统维持运转。

真正的问题,是 Architecture 和 Organization 从来没有把关键知识转化成清晰的边界、统一的概念、Tests、Ownership 和可追踪的 Flow。

知识垄断很少是故意设计出来的。

它们通常出现在复杂度被中心化之后。

中心化复杂度在一段时间里会显得很高效。

直到有一天不再高效。

那时,它就变成了一个有办公室的风险。

微服务确实可以帮助解决其中一部分问题。

但不会自动解决。

如果只是把 SOA 换成更小的 Service,却保留同样的技术建模思路,那么最后得到的只是下一个分布式 Integration Knot——包装得更现代而已。

ESB 可能改名叫 API Gateway。

中心 Integration Knowledge 可能转移到了 Platform Team。

Canonical Model 可能分散成多个 DTO Package。

中心 Orchestration 可能变成同步 Service Call Chain。

而 Frontend 最后还是拿到一堆技术数据,再自己把业务意义拼起来。

恭喜。

大象现在 Cloud-native 了。

问题还在。

如果微服务只是切 Backend Artifact,它就不会真正工作。

只有当它沿着业务职责纵向切分——包含 Product、UX、Frontend、Backend、Tests 和 Operations——它才真正形成有意义的边界。

更直接地说:

如果一个 Microservice 的业务含义必须等到 Frontend 才被重新拼出来,那它就不是一个清晰的业务切分。它只是一个更大架构问题里的小型 Backend 组件。

并不是每个分布式应用都是微服务架构。

有一些非常明显的警告信号。

Service A 改了,Service B、C、D 也必须跟着改。

Release 必须统一协调。

Service 共用数据库。

Service 共用同一个 Domain Model。

一个 UI Flow 需要同步 Call Chain 跨过多个 Service。

团队无法独立做决策。

API Contract 反映的是技术 Table,而不是业务概念。

业务意义要等到 Frontend Mapping 时才出现。

没有清晰的数据归属。

Observability 不足。

Error 跨 Service Boundary 后几乎无法追踪。

这不是一个自治的 Service Landscape。

这是一个带 Deployment Pipeline 的分布式依赖图。

这两者的差别并不学术。

它决定了 Architecture 是提升移动能力,还是阻碍移动能力。

如果所有东西都必须一起改,那就没有真正独立的东西。

如果什么都不独立,就应该诚实地问一句:这种分布式到底还在帮助系统,还是只是增加了新的错误类型?

Network Error。
Versioning Problem。
Timeout。
Retry。
Consistency Question。
跨系统 Monitoring。
地狱般的本地开发环境。

这些成本都可以接受。

前提是别为了一个业务上依然强耦合的架构去支付它们。

为什么糟糕的 Service Boundary 会在 Frontend 暴露出来

Section titled “为什么糟糕的 Service Boundary 会在 Frontend 暴露出来”

Frontend 经常是 Backend 边界问题真正显形的地方。

不是因为 Frontend 有错。

而是因为用户 Flow 在这里终于变得具体。

架构图可以看起来非常优雅。
Sequence Diagram 可以很合理。
Backend 层面的 Service Boundary 也可能显得非常干净。

然后,一个 Screen 出现了。

突然 UI 必须从五个 Service 拼数据。

它需要知道技术 ID、内部 Status Code 和历史 Flag。

它用 Mapping Logic 重新构造业务意义。

Error State 按 API 设计,而不是按用户场景设计。

一个 Button 是否 Enabled,取决于多个 Backend State,但没人能为这个业务状态起一个准确名字。

ViewModel 变成修补错误 Boundary 的 Repair Layer。

UX 使用的词和 API 使用的词对不上。

Tests 验证 Technical Response,却没有验证真正的用户 Flow。

产品决策最后卡在“这个 Service 到底谁 Owner”的讨论里。

到这个阶段,Frontend 已经不再只是建模使用体验的地方。

它变成了 Integration Layer。

只是没有 Integration Layer 应有的地位、授权和工具。

如果你的 Frontend 总是在重新拼接业务关系,那么 Backend Boundary 很可能没有架构图看起来那么“业务化”。

这其实是非常有效的架构测试。

不要问:

“我们有多少个 Service?”

而应该问:

“有多少业务意义,是因为没有任何边界真正建模它,最后只能由 UI 修补?”

如果 UI 必须自己拼业务意义,通常说明边界本身有问题。

业务 Ownership 不会在 Controller 结束

Section titled “业务 Ownership 不会在 Controller 结束”

另一个常见误区是:

某个 Capability 属于 Backend。

不是。

Capability 不属于 Backend。

它属于完整的 Product Flow。

业务 Ownership 不会在 Controller 结束。

真正的业务职责至少包括:

  • Product Problem
  • User Intent
  • UX
  • Frontend
  • Backend
  • Tests
  • Operations
  • Monitoring
  • 业务语言
  • 数据归属
  • API Contract
  • Error Behavior

这并不意味着每个人都必须什么都会。

当然需要专业分工。Frontend、Backend、UX、Product、Test 和 Operations 各自有不同的视角、工具和深度。

但团队必须能够对整个 Capability 负责。

如果一个团队只拥有 Backend Endpoint,却不理解用户 Flow,这个边界是不完整的。

如果 Frontend Team 只是被动消费别人给出的 API,却没有共同参与 API Language、ViewModel 和 Error Behavior 的定义,这个边界是不完整的。

如果 UX 引入的概念,在 API、Tests 和 Monitoring 中完全找不到,这个边界是不完整的。

如果 Tests 只验证技术 Response,而不验证 User Intent,这个边界同样不完整。

Frontend、Backend、UX、Product 和 Tests 并不是互不相关的世界。

它们只是观察同一个业务职责的不同视角。

只有把职责一起切进去,微服务边界才真正有意义。

共同的业务语言并不是 Backend 独有的问题。

它会出现在所有地方。

UX Text。
Label。
Error Message。
ViewModel。
API Contract。
Event。
Tests。
Backend Use Case。
Monitoring Dashboard。
Support Process。

如果 Backend、Frontend、UX、Product 和 Test 对同一个东西使用不同概念,这不是表面上的命名问题。

这是架构问题。

比如:

Backend 叫 Case
Frontend 叫 Ticket
UX 叫“诉求”。
Test 叫“流程项”。
业务部门叫“案例”。
API 最后返回 ProcessItem

每个人都以为自己说的是同一个东西。

直到某天有人改功能。

这时才发现:大家说的几乎是同一件事,但又不完全一样。而这个“不完全一样”,就是 Bug、误解、特殊情况和无休止沟通最喜欢居住的地方。

语言就是架构。

模糊的概念会制造模糊的系统。

这并不意味着整个公司需要一个 Global Enterprise Model。

那只会重新掉进旧陷阱。

好的架构不需要所有地方共用一套全局语言。

它需要的是:在一个业务 Context 内,语言足够明确。

同一个词在不同 Context 里拥有不同含义,完全正常。

有时甚至是健康的。

但在一个业务切分内部,必须清楚知道每个词到底指什么。

不是“大概”。
不是“历史上一直这么叫”。
也不是“这个大家都懂”。

而要清晰到 UX、Frontend、Backend、Tests、Operations 和 Support 都能描述同一个业务现实。

语言就是架构。模糊的概念会制造模糊的系统。

横向架构模型很方便。

Database。
Backend。
API。
Frontend。

非常容易画。

看起来也很整齐。

放在 Slide 上尤其漂亮。

而且能非常稳定地产生 Handover。

问题在于:用户 Flow 根本不是横向运行的。

它纵向穿过整个系统。

一个真实 Product Flow 往往包括:

  • Product Goal
  • User Intent
  • UX Flow
  • UI State
  • Frontend ViewModel
  • API Contract
  • Backend Use Case
  • Data Model
  • Tests
  • Operations
  • Monitoring

如果这些东西被分散到不同职责里,却没有共同语言和清晰 Ownership,每个 Product Change 都会变成 Integration Task。

改一个字段名?协调。

换一种 Status 展示?协调。

把 Error Message 改得更以用户为中心?协调。

Button 只在某些场景可用?没人知道这个业务 State 到底归谁。

这不是 Frontend Problem。

这是 Boundary Problem。

因此,现代架构不应该第一步就问:

“我们怎么切 Service?”

更重要的是先问:

“谁对哪一段业务 User Flow 负责——从 Intent 一直到 Operations?”

然后才决定,它最终应该表现为 Microservice、Module、Self-Contained System、Bounded Context、Event Flow,还是其他形式。

不要先切 Service。

先切职责。

微服务当然可以工作。

但不是因为它们“比较小”。

它们之所以能工作,是因为承载了真实的业务边界。

这里至少需要满足一些并不轻松的条件:

业务 Context 足够清晰。

Context 内的语言足够明确。

团队拥有的不只是 Endpoint,而是完整职责。

数据归属已经澄清。

API Contract 使用业务概念,而不是 Table Structure。

UI 不需要从技术碎片里重建业务意义。

Tests 不只验证 Response,也验证重要用户 Flow 和业务规则。

Deployment 真正独立。

Operations 和 Observability 被认真对待。

Error Behavior 不只从技术角度理解,也从用户角度理解。

最重要的是:

团队能够做决策,而不需要每次修改都像游行一样穿过多个团队。

微服务不是架构魔法棒。

它更像一个放大器。

好的边界会被它放大。
糟糕的边界也会被它放大。

边界清晰时,它带来 Autonomy、团队扩展能力、清晰职责和独立开发。

边界糟糕时,它带来 Latency、协调成本、Monitoring Pain、本地开发问题,以及很多标题里带“Interface”的会议。

糟糕微服务的替代方案,并不自动等于 SOA。

也不意味着重新回到一个什么东西都舒舒服服黏在一起、像数据库芝士火锅一样的巨大 Monolith。

更好的替代方案,是有意识地做架构决策。

有时,模块化单体更合适。

团队还很小。
业务边界还不清晰。
Deployment Complexity 没有带来真正收益。
Operations Maturity 不够。
Observability 还不足。
Data Ownership 还没搞清楚。
快速业务学习比技术分布更重要。

模块化单体完全可以非常干净。

它可以拥有清晰的业务 Module、稳定的内部 Boundary、好的 Tests、明确语言、合理的 ViewModel,并且保留未来 Extract 的可能性。

这往往比“只是因为架构图看起来不够现代,所以强行拆 Service”好得多。

同样地,Self-Contained System 可能合适。

Event-Driven Architecture 可能合适。

API-first 可能合适。

为 Legacy Integration 建一个 Integration Layer 也可能合适。

真正的 Microservice 也可能合适。

甚至,一个 Deployment 内保持业务模块化,也完全可能是最好的选择。

重点不是保护自己最喜欢的 Pattern。

重点是理解系统承受的真实压力。

业务边界。
团队结构。
变更压力。
Operations Maturity。
Data Ownership。
Deployment Requirement。
Testability。
Product Flow。

架构不是信仰问题。

尽管有些讨论确实非常接近宗教战争。

微服务本身并不一定错。

SOA 本身也不一定错。

Monolith 同样不一定错。

真正错误的是,把技术分布误当成业务 Architecture。

错误的是,只切 Backend Service,然后期待 Product Flow 自己在上面自然出现。

错误的是,把 UI 当成表面层,尽管它事实上正在建模业务意义、State 和用户决策。

错误的是,把中心 Technical Model 当成 Product Model。

错误的是,让 Ownership 沿技术 Layer 分配,而不是沿业务 Capability 分配。

错误的是,不认真对待语言。

因为每一条模糊 Boundary,最后都会显形。

经常是在 Frontend。
经常是在 Tests。
经常是在 Operations。
几乎总是太晚。

UI 不是盖在系统上面,它是业务切分的一部分。

UI 不是盖在系统上面的东西。它是业务切分的一部分。

微服务也不是一个纯 Backend 概念。

如果一个边界无法同时承载 Product、UX、Frontend、Backend、Tests 和 Operations,它就还不是真正的业务边界。

它只是被分布式化了。