摘要
你可能见过这样的项目:刚起步就整上微服务框架、Kafka 消息中间件一把梭,再加上好几层抽象工厂 + 策略模式。等上线后才发现,系统只有两三个接口,压根没人用。
这些“自我感动式”的开发,其实是一种隐形浪费。它不是技术能力的展现,而是一种“过度假设未来”的风险决策。本文就来聊聊,怎么在架构设计中避免掉进“过度工程”的坑,选对“当下最合适”的解法。
引言
在很多开发讨论中,经常会出现这样的对话:
- “咱们加个事件总线吧,不然以后不好扩展。”
- “这个模块最好先拆微服务,以后独立部署才方便。”
- “要不我们写个接口层 + 抽象层吧,万一换实现也简单。”
这些听起来都很“有道理”,但问题是:
你确定那个“以后”真的会来吗?
架构设计,不能为了“展示设计能力”而去设计。而应该是——为当前的实际问题找最直接、最简单的解决方案,同时保留未来演进的可能。
架构设计里那些常见的“坑”
典型“过度工程”场景盘点
下面这些操作你可能都见过:
- 项目刚立项,产品都没写完,就先上 DDD + CQRS 架构;
- 接口调用还没超过 10QPS,就搭建 Kafka + Redis + RabbitMQ 三件套;
- 把一个简单的“用户积分增加”逻辑,写成工厂模式 + 策略模式 + 抽象接口 + 依赖注入;
- 三个人的团队,把项目拆成六个微服务部署,还搞 CI/CD 全自动流程;
这些设计在“大公司大业务”里可能确实需要,但对很多项目来说,是 沉没成本+运维负担+认知负债 三杀。
为什么会这样做?
- 怕未来被打脸:“我们现在不考虑,将来肯定会痛苦。”
- 试图显得专业:“我会架构设计,抽象能力强。”
- 兴趣驱动:“我最近刚学完微服务,想找机会试试。”
- 写着写着就上头了:“加个接口好像也不麻烦,再加个中间层吧?”
“设计是否真的必要”的判断准则
提出三个关键问题
在开始设计之前,问自己:
- 这个设计是为了解决当前的问题吗?
- 有没有证据表明这个问题“马上就要来”?
- 有没有一个更简单的方法,能先解决问题并保留演进空间?
设计越多,维护成本越高;模块越多,沟通成本越高。
识别 YAGNI 现象
YAGNI(You Aren’t Gonna Need It)原则的核心是:
“别为未来做设计,除非它真的已经来了。”
比如:
- “我们加个缓存接口吧,以后可以换 Redis/Memcached。”但现在连用户量都没破千;
- “做个抽象层吧,说不定后面接入第三方支付。”但其实你短期根本不打算加;
这些全都是典型的 YAGNI。
用实际例子说话:一个“用户积分”服务的两种写法
背景需求
业务需求非常简单:
- 给用户加积分;
- 输入一个 user_id 和积分值;
- 返回这个用户当前的积分总值。
这类接口在很多 App、运营系统、活动平台里都很常见。
写法一:过度工程版本(反例)
# service.py
class PointsService:
def add_points(self, user_id: int, points: int) -> int:
raise NotImplementedError
class PointsServiceImpl(PointsService):
def __init__(self):
self.points_db = {}
def add_points(self, user_id: int, points: int) -> int:
self.points_db[user_id] = self.points_db.get(user_id, 0) + points
return self.points_db[user_id]
# factory.py
def get_points_service() -> PointsService:
return PointsServiceImpl()
# main.py
service = get_points_service()
print(service.add_points(123, 10))
这个结构你肯定不陌生:接口 -> 实现 -> 工厂模式。
问题是:
- 现在根本没多个实现;
- 工厂也没什么逻辑,只是返回一个类;
- 所有模块拆得很散,不利于快速开发或测试。
写法二:适度设计版本(推荐)
# 简洁明了版本
points_db = {}
def add_points(user_id: int, points: int) -> int:
points_db[user_id] = points_db.get(user_id, 0) + points
return points_db[user_id]
# 测试用例
if __name__ == "__main__":
print(add_points(123, 10)) # 输出: 10
print(add_points(123, 5)) # 输出: 15
优势:
- 函数式结构清晰、轻量;
- 易于快速测试、上手;
- 不失灵活性,未来可以轻松迁移成 class 结构;
- 更适合 MVP 阶段快速验证。
QA 环节:你可能会问的几个问题
Q1:那以后业务复杂了怎么办?
答:架构是可以演进的。你今天用一个函数实现,明天拆 class,一周后提取模块,一月后可以拆服务。
Q2:怎么判断是不是过度设计?
答:看使用频率、实现复杂度和维护成本之间的关系。如果你写了 100 行,只为支撑一个调用一次的接口,那就该反思一下。
Q3:是不是完全不能做抽象?
当然不是。适度抽象依然重要,但要把握节奏。抽象是为了解耦,不是为了堆技术词。
总结
架构设计的终极目标,不是“用最炫的技术”,而是“用最低的成本,跑通正确的业务”。
一个好的工程师,不是技术最潮的那一个,而是能判断什么时间该用什么设计的那一个。
切记:写代码是为了解决问题,不是为了写代码本身。
未来展望
未来系统肯定会越来越复杂,需求也会不断增长。但你现在最重要的任务,是:
- 活下来(先跑通 MVP)
- 跑得稳(上线不出事)
- 改得动(迭代不崩盘)
架构设计,不是一次性拍板的定案,而是伴随业务和团队成长的“长期策略”。
真正的技术力,是看你如何处理“复杂问题”,而不是你堆了多少“复杂技术”。