system column十三Tech
← 返回技术专栏
TECH

范式驱动开发:决定系统上限的,不是代码量,而是你如何表达问题

很多系统后期失控,不是因为需求太多,而是因为一开始就用错了表达问题的方式。本文系统拆解什么是范式驱动开发、它与设计模式和 DDD 的关系,以及如何在真实项目中判断并落地合适的开发范式。

架构设计软件工程DDD设计模式系统设计十三Tech

大家好,我是十三。

很多系统在早期都看起来没什么问题。

需求来了,建表;接口来了,写 service;逻辑复杂一点,就多加几个 if-else;线上出 bug 了,再补几个判断。短期看,这种方式很高效,需求也确实交付了。但只要系统开始进入复杂区,你很快就会发现:

真正拖垮一个系统的,往往不是功能越来越多,而是你从一开始就用错了表达问题的方式。

同样是做业务系统,有的团队越做越顺,新需求只是扩展;有的团队越做越重,每个需求都像是在拆雷。很多人以为这只是代码质量、架构经验或者团队成熟度的差异,但在我看来,更底层的分水岭其实是:

这个团队到底是在“功能驱动写代码”,还是在“范式驱动做开发”。

今天这篇文章,我想系统聊透四个问题:

  1. 什么是范式驱动开发
  2. 它和设计模式、架构风格、DDD 到底是什么关系
  3. 它为什么决定了系统后期的复杂度曲线
  4. 在真实项目里,到底该怎么落地

一、什么是范式驱动开发?

先给一个尽量准确的定义:

范式驱动开发(Paradigm-Driven Development)是指:在进入具体实现之前,先识别系统的主要复杂性来源,再选择最适合承载这种复杂性的建模范式,并让代码结构、模块边界和演进方式围绕这个范式展开。

这句话有点长,但里面有三个关键词非常重要:

  • 复杂性来源
  • 建模范式
  • 围绕范式展开实现

换句话说,范式驱动开发的核心不是“怎么写代码”,而是先回答:

这个问题,本质上应该被表达成什么?

它可能是:

  • 一个状态流转问题
  • 一个规则组合问题
  • 一个领域边界问题
  • 一个异步协同问题
  • 一个读写职责分离问题

如果这个判断做错了,后面代码写得再勤奋,系统也会越来越拧巴。

所以范式不是“高级写法”,也不是“架构黑话”。它的本质是:你组织复杂性的第一原则。

二、先把边界说清:范式,不等于设计模式,也不等于技术框架

如果不把这几个概念切清,这篇文章很容易变虚。

1. 范式,不是设计模式

设计模式解决的是局部设计问题

比如:

  • 策略模式,解决一组算法可切换的问题
  • 责任链模式,解决一串处理器解耦的问题
  • 工厂模式,解决对象创建的问题

这些都很重要,但它们回答的是“这一段代码怎么组织”。

而范式回答的是更上一层的问题:

这个系统的主要复杂性,应该由什么结构来承载?

比如一个营销系统,真正的问题可能不是“策略模式怎么写”,而是:

  • 它到底是不是一个规则系统?
  • 规则之间有没有优先级、互斥、依赖关系?
  • 规则是研发硬编码,还是商家可配置?
  • 规则变化的频率,是否已经高到值得独立成规则层?

这就是范式层面的判断。

设计模式是战术,范式是战略。

2. 范式,不是架构风格

微服务、单体、六边形架构、分层架构,这些更偏向技术组织形式

它们回答的是:

  • 服务怎么拆
  • 接口怎么隔离
  • 依赖怎么反转
  • 部署怎么组织

这些属于架构层面的选择。

但你就算用了微服务,也不代表你选对了范式;你就算做成单体,也不代表范式落后。

比如一个审批系统,哪怕是单体,只要你把“状态跃迁”和“节点规则”建模清楚,它依然可能比一个错误拆分的微服务系统更健康。

所以:

  • 架构风格解决的是技术结构问题
  • 范式选择解决的是业务复杂性表达问题

3. DDD 是一种重要范式,但不是全部

很多人看到这里会说:这不就是 DDD 吗?

只对一半。

DDD 确实是一种非常重要的范式,尤其适合复杂业务语义和边界冲突明显的系统。它强调:

  • 统一语言
  • 限界上下文
  • 聚合边界
  • 领域模型优先

但不是所有复杂性都来自领域边界。

有些系统的复杂性主要来自状态变化,比如审批流、工单流、履约流。
有些系统的复杂性主要来自规则组合,比如营销、风控、权限。
有些系统的复杂性主要来自事件协同,比如交易后链路、异步通知、库存同步。

这时候,DDD 可能只是其中一层,而不是全部答案。

所以我更愿意把 DDD 放在一个更大的框架下理解:

DDD 是范式库里的一个核心成员,但范式驱动开发关注的是“如何为问题挑选正确范式”,而不是默认所有问题都用 DDD。

三、为什么很多系统会越做越乱?

因为大多数不是被建模出来的,而是被需求一层层“糊”出来的。

我们看一个很典型的案例:活动系统。

第一阶段:需求很简单

产品说,做一个满 100 减 10。

于是代码很自然:

if order.Amount >= 100 {
    discount += 10
}

这时候没有任何问题。

第二阶段:规则开始增加

接下来需求一个个来:

  • 新用户首单额外减 5
  • 某些类目不参与活动
  • 优惠券和满减不能叠加
  • 不同渠道活动不同
  • 不同商家活动优先级不同
  • 某些活动仅限会员
  • 活动支持配置开始时间和结束时间

你会发现,系统的本质已经变了。

它不再是“算一个金额”的问题,而开始变成:

  • 规则筛选
  • 规则命中
  • 规则冲突判定
  • 规则优先级排序
  • 规则组合执行
  • 规则配置化管理

也就是说,它已经从一个“计算逻辑”问题,升级成了一个规则系统问题。

第三阶段:团队还在用旧范式写新问题

这时候,如果你还在原来的 service 里不断追加 if-else,最后就会出现典型后果:

  • 规则散落在多个 service
  • controller 里偷偷写了一部分校验
  • 前端为了展示,又复制一遍规则判断
  • SQL 里还有一部分过滤条件
  • 配置表存在,但只是辅助,真正逻辑还在代码里
  • 新增一种活动玩法,需要改五六个地方

最后最危险的不是“代码丑”,而是:

你已经说不清系统真正的决策逻辑在哪里。

这就是很多系统后期难维护的根本原因:

不是需求太复杂,而是系统还在用过去的范式承载今天的问题。

四、范式驱动开发,真正驱动的是什么?

我认为它至少驱动三个层面的决策。

1. 驱动你如何建模业务

先决定“这个系统世界由什么构成”。

还是以活动系统为例。

如果你把它理解成“订单金额加减法”,那系统核心就是一堆计算逻辑。
如果你把它理解成“规则决策系统”,那系统核心就会变成:

  • 规则定义
  • 规则条件
  • 规则动作
  • 规则优先级
  • 规则冲突策略
  • 规则执行链路

这两种理解都会写出能跑的代码,但后续演进成本天差地别。

2. 驱动你如何划边界

范式一旦不同,模块划分也会不同。

如果活动系统的主范式是规则驱动,那么通常就应该显式存在这些边界:

  • 活动定义层
  • 规则表达层
  • 规则执行层
  • 冲突裁决层
  • 配置管理层

而不是只有一个庞大的 PromotionService

所以边界不是随便分的,它应该服从主范式。

3. 驱动你如何演进系统

一个系统真正的考验,不是第一次做功能,而是第十次变更时还能不能稳。

范式选得对,新需求大多表现为:

  • 新增规则
  • 新增状态
  • 新增事件
  • 新增边界映射

范式选得不对,新需求就会表现为:

  • 修改旧逻辑
  • 入侵多个模块
  • 补更多特判
  • 引发一串回归风险

这就是为什么成熟团队看待“扩展性”时,关注点不在代码量,而在主范式是否稳定

五、一个完整案例:活动系统为什么应该从 if-else 升级到规则驱动?

这一段我想讲具体一点,不然“可落地”容易落空。

1. 最初的问题表达方式

很多活动系统一开始都是这么起步的:

  • 输入:订单、用户、渠道、商品
  • 输出:优惠金额
  • 实现:若干个 if-else

这在早期完全合理,因为需求少、规则少、变化慢。

2. 什么时候说明你该升级范式了?

当你出现以下现象时,说明它已经不再只是简单逻辑,而是规则系统:

  • 同一订单可能命中多条活动
  • 规则之间存在互斥、叠加、优先级
  • 非研发角色要配置活动
  • 活动规则变更频繁
  • 新增玩法越来越像“配置组合”而不是“写新逻辑”
  • 排查一个订单为什么享受了某优惠变得困难

这六条里,只要命中三四条,基本就该警觉了。

3. 规则驱动后,系统应该长什么样?

这时候你至少应该把核心抽象切出来。

规则定义

规则描述“什么条件下触发什么动作”。

例如:

  • 条件:订单金额 >= 100 且用户是新客
  • 动作:减 15

规则条件层

负责判断是否命中:

  • 金额条件
  • 用户标签条件
  • 商品条件
  • 渠道条件
  • 时间条件

规则动作层

负责决定命中后的结果:

  • 直减
  • 折扣
  • 赠券
  • 包邮

冲突裁决层

负责处理规则之间的关系:

  • 是否可叠加
  • 谁优先
  • 同类型取最大还是可累加
  • 券和活动如何互斥

执行引擎

负责把“候选规则集合”变成“最终优惠结果”。

4. 范式升级带来的直接收益是什么?

不是“看起来高级”,而是四个非常实在的收益:

  • 新增玩法更像加规则,而不是改旧代码
  • 排查问题时更容易解释决策路径
  • 配置化能力自然出现
  • 研发、产品、运营可以围绕同一套规则语义协作

这就是所谓的“可落地”。因为它不是抽象变漂亮了,而是后续成本真的变低了。

5. 如果你不升级,会怎样?

系统通常会逐步出现以下症状:

  • 规则散落在多个地方
  • 线上 bug 多数来自特判冲突
  • 一个需求改动带来多个链路回归
  • 只有老员工知道哪里真正生效
  • 配置平台越来越像“假配置”,因为核心逻辑还在代码里

当你看到这些现象时,实际上已经不是“要不要重构”的问题,而是:

你是不是一直在用错误范式拖着系统往前走。

六、怎么判断一个问题适合什么范式?

这是最关键的落地点。

我给你一个实用判断表。

主要复杂性来源 更适合的主范式 典型场景 关键信号
状态流转复杂 状态驱动 / 工作流驱动 审批、工单、履约 状态多、跃迁规则多、回退/撤销复杂
规则变化频繁 规则驱动 营销、风控、权限判定 条件组合多、优先级多、配置需求强
业务边界和语义冲突明显 领域驱动 ERP、用户中心、交易域 同名概念跨团队含义不同、边界混乱
异步链路复杂 事件驱动 交易后处理、库存同步、通知 多系统联动、最终一致性、重试补偿多
读写诉求差异大 CQRS / 读写分离 报表、搜索、运营分析 写模型稳定但读模型多样且重查询
流程线性且规则稳定 面向过程 / 简单分层 后台工具、小型内部系统 变化少、生命周期短、复杂度低

这个表最重要的不是“背答案”,而是建立一个习惯:

先看复杂性来自哪里,再决定让谁做主范式。

不是哪个范式高级就上哪个,而是哪个范式最贴近问题本质,就让它主导。

七、范式驱动开发,真正的落地步骤是什么?

我建议按五步走,比单纯讲理念更有效。

第一步:识别系统的主要复杂性

先不要急着建表和拆接口。

先回答:

  • 系统最容易失控的地方在哪里?
  • 复杂性来自状态、规则、边界、事件,还是查询?
  • 哪一类变化最频繁?
  • 哪一类错误最贵?

如果这个判断没做,后面就是盲开。

第二步:确定主范式,而不是堆多个范式

一个系统可以同时存在多种手段,但通常要有一个主范式。

比如:

  • 活动系统,以规则驱动为主
  • 审批系统,以状态驱动为主
  • ERP,以领域驱动为主
  • 异步交易后链路,以事件驱动为主

这一步的重点是:

主范式负责承载主要复杂性,其他范式只做辅助,不要互相抢地盘。

第三步:让代码结构体现范式,而不是只写在方案文档里

真正的落地一定要体现在代码上:

  • 目录结构是否反映业务语义
  • 状态变化是否有统一入口
  • 规则是否有独立表达层
  • 事件是否是显式对象
  • 模型边界是否防止概念泄漏

如果你说系统是规则驱动,但规则还埋在 service 里,那不叫落地。

第四步:用新增需求验证范式是否成立

一个范式好不好,不能看第一次实现有多优雅,要看第 N 次迭代是否依然稳定。

你可以用这几个问题做验收:

  • 新增一个规则,要改几个地方?
  • 修改一个状态流转,会不会波及无关模块?
  • 一个新人能否快速定位决策逻辑?
  • 同一个业务概念,在系统里是否只有一个主表达?
  • 问“为什么这个结果会这样”,系统能否讲清楚?

如果这些问题的答案都比较健康,说明范式大概率是对的。

第五步:持续观察“复杂性是否被关进笼子里”

范式驱动开发不是一次性决策,它需要持续验证。

真正好的范式会让复杂性:

  • 集中
  • 可解释
  • 可扩展
  • 可替换

而不是继续向 controller、service、SQL、前端逻辑四处泄漏。

八、怎么判断你是不是选错了范式?

这部分非常实用,因为很多问题不是不会做,而是做错了却没有意识。

如果系统出现下面这些症状,大概率说明范式没选准。

1. 新需求总是表现为“修改旧逻辑”

如果每次新增功能都要回头改一堆既有分支,那说明系统没有为变化预留正确表达空间。

2. 规则和流程散落在多个层级

controller 有一点,service 有一点,数据库有一点,前端还有一点。
这通常意味着系统没有明确“复杂性归属”。

3. 同一个业务概念出现多个版本

比如“订单状态”在订单服务、履约服务、前端页面里各有一套解释。
这往往是边界范式出了问题。

4. 系统能跑,但讲不清

最危险的系统不是报错多,而是“只能运行,不能解释”。

你问它为什么这样,它只能回答:“历史就是这么写的。”

5. 老员工离开后,改动成本陡增

如果系统高度依赖个别人的隐性认知,而不是显式模型,说明范式没有沉淀成结构。

这几个信号,本质上都在说明一件事:

你的复杂性没有被一个正确范式收束,而是在系统里自由扩散。

九、范式驱动开发,不等于一上来就搞大架构

这里我必须强调一个边界,不然这篇文章很容易把人带偏。

范式驱动开发不是过度设计的许可证。

不是所有系统都值得上“高级范式”。

如果你做的是:

  • 生命周期很短的后台工具
  • 逻辑稳定的小型内部系统
  • 边界简单的单流程应用

那用简单分层、清晰对象、少量模式,往往就是最优解。

真正成熟的判断不是“上不上范式”,而是:

这个问题的复杂度,是否已经值得我引入更强的表达方式。

能用简单对象模型解决的,就不要先上规则引擎。
能用轻量状态机解决的,就不要一开始就上 BPM。
能在单体内划清边界的,就不要急着微服务化。

范式驱动开发的核心不是“重”,而是“准”。

十、从功能实现者到系统建模者,这是工程师认知层级的跃迁

为什么有些工程师写了很多年代码,依然停留在“接需求—写接口—改 bug”的循环里?

因为他的关注点始终停留在“功能怎么实现”。

而真正开始拉开差距的工程师,会习惯性地问:

  • 这个需求背后重复出现的模式是什么?
  • 它真正的复杂性来自哪里?
  • 现在是在解决问题,还是在继续堆补丁?
  • 这个系统最适合由哪种范式来主导?

这是一个非常重要的分水岭。

低阶开发,关注代码怎么写。
中阶开发,关注模块怎么分。
高阶开发,关注问题应该如何表达。

而我理解的范式驱动开发,本质上就是这最后一层能力。

总结

范式驱动开发,不是为了让系统看起来更高级,而是为了让复杂性有秩序地落位。

它真正回答的问题不是“这段代码怎么写”,而是:

这个系统里最复杂、最易变、最容易失控的部分,究竟应该由什么结构来承载。

当你把一个规则问题建模成规则系统,把一个状态问题建模成状态系统,把一个边界冲突问题建模成领域问题,你会发现很多原本靠补丁维持的复杂度,开始变得可解释、可扩展、可演进。

在我看来,优秀工程师和普通工程师的差距,很多时候不在于会不会写代码,而在于能不能透过需求表象,看见问题真正应当归属的范式。

代码只是最后一层落地。
而范式,才是决定系统命运的第一推动力。