一、仅在必要时拆分系统
我们曾有过一个典型的“超前设计”案例:为了追求从上线第一天起就具备可扩展性,一个支付系统从一开始就采用了微服务架构。想法很美好,但现实呢?
图片
结果就是,即便在流量极低的情况下,处理一次支付请求也需要触发多次网络调用。这带来了两个直接问题:首先,任何一个服务节点出现故障,整个支付流程就会立刻中断;其次,调试过程简直是一场噩梦,工程师不得不在海量日志中穿梭,并且需要跨多个团队协调排查。
更麻烦的是,哪怕只是做一个简单的功能改动,也需要在多个相关联的服务中分别部署,效率大打折扣。
于是,我们决定做一次“减法”。
图片
核心思路是:保留清晰的逻辑边界,但消除不必要的物理隔离。我们将部分紧密耦合的服务合并,大幅减少了潜在的故障点和网络开销。效果立竿见影——部署变得简单,调试速度也快多了。当然,这并非否定微服务。当业务真正增长,某个模块的负载成为瓶颈时,我们再将其清晰地拆分出来,成为独立的微服务。这个教训很深刻:试图包罗万象、一步到位的架构,往往什么都处理不好。
二、避免过度抽象
另一个常见的陷阱是“抽象过度”。我们曾接手一个难以维护的订单管理系统,第一反应就是用“抽象”来整理它。于是,泛化服务、共享仓库、各种基类和接口被引入,旨在“标准化”一切行为。
结果却适得其反,系统变得比之前更难以理解。
图片
想要理清一个订单流程,你不得不在多个抽象层之间跳转,业务规则被分散隐藏在各个角落。即便是微小的修改,成本也变得极高,因为牵一发而动全身。抽象确实消除了代码重复,但同时也抹杀了业务含义,让系统变得晦涩。
我们随后调整了策略。
图片
新的方案是:用明确的、具体的代码来定义工作流。订单创建、定价计算、履约逻辑——这些核心步骤都直观地体现在代码中。虽然可能存在一些有意的、清晰的代码重复,但好处是巨大的:变更的影响范围被严格限定在本地,一旦出现故障,追踪根源也变得轻而易举。这印证了一个道理:简单的系统,故障一目了然;复杂的系统,故障则难以捉摸。
三、 架构设计应以简洁为主,而非花哨
追求技术上的“优雅”有时会走入歧途。在一个结账系统中,我们曾全面采用事件驱动架构,流程中的每一步都通过发布和消费事件来衔接。
在白板上,这套设计看起来无比优雅和松耦合;但在生产环境中,它带来的却是持续的运维痛苦。
图片
问题出在哪里?你会发现,大量的讨论都围绕着事件顺序、重试机制和消费者延迟展开。更关键的是,没有一个服务对订单的最终状态负全责。一旦某个环节失败,订单就会陷入“部分成功”的灰色地带,必须人工介入处理。这套架构,实际上是用核心流程的清晰度,换取了理论上(但未必用得上的)灵活性。
我们的解决方案是回归简洁。
图片
我们用一套清晰的同步服务接管了核心结账流程:一个服务负责验证购物车、处理支付并返回明确的结果——要么成功,要么失败。事件机制依然存在,但被降级到辅助角色,仅用于发送通知或更新分析数据。核心业务流程恢复了线性与可控。现在,任何故障都会立刻暴露,订单状态非黑即白,再也没有模棱两可的中间状态。一个值得警惕的信号是:当一种架构需要长篇大论才能解释清楚其工作原理时,它很可能已经设计得过于复杂了。
四、你不需要它 (YAGNI)
“你不需要它”这条原则,在实践中被违反的次数远超想象。我们曾遇到一个功能非常单一的产品:用户提交反馈,管理员进行审核。就这么简单。
但你看它的架构,却复杂得令人费解。
图片
理论上,这套设计是在为“未来”做准备。但实际上,它是在为一个永远不会到来的“未来问题”做优化,并为此背负了所有不必要的复杂性:单个服务内堆砌了多层架构;基于事件的提交流程;反馈内容、状态、审计日志分别存入不同的数据表;甚至还引入了功能标志来保护那些未完成或根本用不到的功能。
所有这些复杂性都挤压在一个服务里,没有解决任何现实问题,纯粹是基于臆测的过度设计。
简化之后的架构是这样的:
图片
我们移除了那些无用的抽象层、多余的数据表和冗余的基础设施。系统立刻变得清晰、直白。事实上,大多数系统最终不堪重负乃至崩溃,往往不是因为解决了真正的问题,而是因为背负了太多为“从未出现的问题”所做的决策。
五、配置过多并非总是好事
最后一个陷阱是对“灵活性”的误解。某个内部系统为了追求极致的灵活,几乎将所有行为都交给了配置驱动:功能标志、环境变量、多份配置文件共同左右着核心逻辑。
结果就是,绝大多数线上事故的根源不是代码Bug,而是某处错误的配置。

表面上看,这赋予了运维极大的灵活性;但实际上,它引入了大量隐藏的复杂性:运行时需要从多个来源加载配置;功能标志控制了关键的执行路径;环境特定的行为散落在代码库各处。要理解系统“如何”运行,你必须先了解它“在哪里”以及“如何”被部署。
简化之后,我们确立了新的规则:
图片
首先,清除代码中硬编码的默认值,让行为更明确。其次,功能标志严格限定于短期、临性的发布策略,而非长期业务逻辑开关。最后,配置项仅用于管理真实存在的环境差异(如数据库地址)。减少这些“活动部件”之后,部署安全性提高了,系统行为变得可预测,代码本身也更容易被读懂。一个常见的反模式是:当灵活性被置于至高无上的地位时,清晰度往往是第一个被牺牲掉的品质。
说到底,追求简洁的架构绝不意味着设计薄弱或偷工减料。恰恰相反,它意味着有勇气和智慧去剔除那些不必要的复杂性,从而打造出一个在持续演进中依然易于理解和维护的系统。真正稳固的系统,往往始于简洁;其复杂性,是在解决真实业务挑战的过程中,随着时间推移而逐步、有机地积累起来的。
作者丨Vinod Pal 编译丨dbaplus社群
来源丨网址:https://medium.com/javarevisited/how-simpler-architectures-made-me-a-better-senior-developer-8873a44e0a4c
