TechForge 的新指南警告称,许多初创的微服务项目最终会变成“分布式单体”,在没有获得任何扩展优势的情况下,却带来了网络调用的延迟。该文章敦促工程团队从一个稳固的单体架构开始,只有在出现明确的扩展或所有权需求时才将其拆分。
为什么团队会急于转向微服务
微服务的吸引力显而易见:独立的微服务、分离的部署,以及按需扩展应用程序每个部分的承诺。创业文化和近期的成功案例已将这种模式变成了现代工程的勋章。然而,过早拆分单体往往会创造出一种新型的单体——由数十个联网组件组成的单体。代价是什么?更高的延迟、更难的调试以及更多的运维开销,而最初的优势却遥不可及。
第一个错误:名义上的单体
团队经常将系统贴上“基于微服务”的标签,但实际上仍保留着单一的代码库和共享的数据库。结果是一系列紧密耦合的模块,它们仍然通过 HTTP 或 RPC 进行通信。该指南将其称为“分布式单体”。其痛点与传统单体一致——紧密耦合,且难以在不影响其他部分的情况下更改某一部分——此外还增加了网络跳转带来的延迟。
更好的做法: 首先构建一个清晰的单体架构。定义明确的模块边界,保持数据层统一,并确保应用程序可以作为一个整体进行测试和部署。只有当某个模块需要独立扩展或由不同的团队负责时,才将其提取为独立的微服务。
按技术层拆分 vs 按业务能力拆分
另一个常见的错误是按照技术关注点(如 UI、业务逻辑或数据访问)来划分服务。这会导致单个操作必须经过一系列服务的链式调用,从而增加了响应时间并创建了脆弱的依赖图。
更好的方法: 围绕“订单”、“支付”或“库存”等业务能力来组织服务。让每个业务能力拥有自己的数据和 API,从而消除请求在不同层级间跳转的需求。
数据所有权至关重要
当两个服务向同一个数据库表写入数据时,它们就不再是独立的了。该指南强调,一个服务绝不能直接查询另一个服务的表;它应该始终通过该服务的公共 API 进行访问。共享数据库会将服务捆绑在一起,破坏隔离性,并使模式(schema)变更变成一场协调噩梦。
同步 HTTP 并非万能方案
依赖同步 HTTP 进行每一次交互,会使整个系统容易受到单个慢速服务的影响。如果服务 A 在向客户端返回响应之前必须等待服务 B 的响应,那么 B 的任何延迟都会传播到 A,并最终影响到用户。
替代模式: 对于不需要立即响应的任务,使用异步消息机制。消息队列或后台作业可以让服务移交工作并继续处理,从而保持整个系统的韧性。
接受最终一致性
传统的关系型数据库提供 ACID 事务——原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。一旦跨越服务边界,这些保证就会消失。试图强行使用两阶段提交(two-phase commits,一种试图让分布式事务表现得像本地事务一样的协议)会导致复杂性和不稳定性。
该指南推荐使用 Saga 模式(一系列补偿操作)或发件箱模式(outbox pattern,即服务将事件写入本地表,随后再发布这些事件)。这些方法承认数据可能会暂时不同步,并设计业务逻辑来处理这些间隙。
从第一天起就为失败做好准备
单个服务中的 Bug 不应导致整个系统崩溃。应实现超时机制以避免无限等待,实现带有退避(back-off)机制的重试以处理瞬时故障,并实现熔断器(circuit breakers)以在故障服务恢复前停止对其调用。在生产环境发生故障后再添加这些防护措施就太晚了;它们应该包含在初始设计中。
可观测性是不容妥协的
如果日志散落在许多容器中,调试分布式系统几乎是不可能的。集中式日志、聚合指标和请求级的关联 ID(correlation IDs)可以让工程师追踪单个用户请求在多个服务之间的流动。链路追踪工具可以将调用图可视化,从而更容易定位性能瓶颈和故障。
在开始阶段保持基础设施轻量化
Kubernetes, while powerful, brings a steep learning curve and operational overhead. For a handful of services, Docker Compose provides enough orchestration to spin up the entire stack locally. Only when traffic patterns, deployment frequency, or team size demand it should a more complex platform be introduced.
Align services with team ownership
Microservices were partly invented to let small, autonomous teams own the full lifecycle of a service. If a single team is responsible for ten services, coordination costs rise dramatically, eroding the intended benefits. The guide suggests that teams of fewer than ten people may be better served by a monolith, preserving simplicity while still allowing modular development.
The counter-argument: when microservices shine
The guide does not claim that microservices are inherently bad. In environments where different parts of an application have wildly different scaling requirements, or where regulatory constraints demand strict data isolation, the pattern can provide real value. Large organizations with multiple product lines often find that independent services reduce cross-team friction and enable faster release cycles.
The key is intentionality. If a team adopts microservices because they need to handle millions of requests per second for a specific feature, or because a new product line must be owned by a separate business unit, the added complexity is justified. The guide’s warnings target cases where the decision is driven by hype rather than concrete requirements.
What to watch for next
As more companies adopt cloud-native stacks, tooling around service mesh, distributed tracing, and automated canary deployments continues to mature. These advances lower the operational barrier but do not eliminate the fundamental design choices highlighted in the guide. Teams should monitor the evolution of observability platforms and async messaging frameworks, but still start with a clear justification for each service they spin up.
Takeaway
Microservices are a means to an end, not an end in themselves. Begin with a well-structured monolith, give each service true ownership of its data, use asynchronous communication where possible, and embed resilience and observability from the first line of code. When the business case is clear, break out services deliberately; otherwise, keep the architecture as simple as the problem demands.
