这篇博文会有点特殊。标题只有《双封装理论》——知行篇,没有任何正文、关键词和摘要。也就是说,我需要根据这个标题本身,以及“双封装理论”这个热词,去反向推演:这到底在讲什么,能解决什么问题,背后是一套什么样的方法论。
我先把结论放在前面:双封装理论,讨论的是“认知模型”与“执行模型”之间的纠缠问题。 “知”是一套,“行”是另一套。真正让项目痛苦的不是单点上的复杂度,而是“知”和“行”在系统演进中被强行耦合在一起,导致改动一个字段要牵动十几个模块,调整一个业务规则要重新上线整个服务。双封装理论主张把“知”和“行”各自做成独立封装,再通过一个显式的映射层把它们衔接起来。这套思路既能用在软件架构上,也能用在团队协作和知识管理上。
如果你正被“业务规则改了但代码不知道”“文档写着 A 但系统跑的是 B”“AI 能说出正确逻辑却执行错误动作”这类问题折磨,这篇文章就是写给你看的。
1. 双封装理论到底在封装什么:剥离“知道什么”和“怎么做”
我第一次听到“双封装”这个词,是在一个做供应链系统的团队里。他们的订单状态机写得极其混乱:数据库中 status 字段有 12 个取值,代码里 if-else 判断有 40 多处,业务方口中的“已审核”和研发理解的“已审核”还不是一回事。最要命的是,每次产品经理修改业务规则,研发都得顺着代码把状态流转全部捋一遍,改完这一处,不知道哪一处又会崩。这就是典型的“知”和“行”没有分离。
所谓“知层”,指的是系统对业务领域的理解,也就是领域模型。它回答的问题是:这个业务世界里有哪几种实体?它们之间有什么关系?有哪些业务规则和约束?在一套订单系统里,“知”包括订单是什么、订单有哪些状态、状态之间允许哪些流转、什么样的订单可以被取消。这些东西本质上是知识,是可以通过文档、UML 图、规则配置来表达的。
所谓“行层”,指的是系统对外部世界产生的影响,也就是执行逻辑。它回答的问题是:接收到一个命令之后,系统实际要做哪些操作?要调哪些外部接口?要写哪些表?要发哪些消息?在订单系统里,“行”包括“取消订单”这个动作要执行的具体代码逻辑:检查库存锁定、调支付平台退款、通知仓库拦截发货。
大部分系统的问题,就是把“知”和“行”混在同一段代码里。一个 Service 方法里,既写了对订单状态的校验规则(这是知),又写了数据库更新语句和第三方调用(这是行)。表面上看起来挺紧凑,但规则一旦变多,这段代码就会膨胀成无人敢动的怪兽。因为你在同一个封装里同时承载了两个维度的变化,而这两个维度的变化频率和方向往往是不同的。
双封装理论的核心主张是:把“知”固化成一份可独立校验、可版本化、可追溯的模型,把“行”收敛成一组只依赖输入和输出的执行器,两者之间不直接调用,而是通过映射关系对接。 你可以把“知”想象成大脑中的地图,“行”想象成开车动作。地图更新和驾驶技术是两回事,你换一张地图不需要重新学开车,你升级驾驶技术也不需要重新画地图。但绝大多数系统的现状是:地图和驾驶技术被糊在了一起,地图改了,车就得重造。
从工程实践来看,这套理论和领域驱动设计(DDD)有相通之处,但强调的重点不一样。DDD 强调的是用统一语言建立领域模型,双封装理论更强调“知行分离”带来的结构强制力:模型层封装了一整套领域知识,执行层封装了一整套技术动作,两者之间只通过事件、命令或 DTO 传递必要信息,谁也别想渗透到谁的内部去。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 知层封装:让业务规则成为可以“被测试的知识”
知层封装的本质,是把“业务规则”这种容易散落在各处的东西,收敛到一个相对稳定的边界内。我在实践中会把知层拆成三个部分来做,这样既不会过度设计,又能保证规则不会漏得到处都是。
2.1 领域规则如何变成显式代码
第一个部分是领域规则本身。比如“已付款订单不能取消”“退款金额不能超过订单实付金额”“会员折扣和优惠券不能叠加使用”,这些都是业务知识。它们的共同特点是:不依赖任何外部系统,不涉及任何数据库操作,纯粹是对某个输入状态的判断。
我推荐的落地方式是把它们封装成领域服务或规则对象。下面是一段用 Python 写的订单取消规则示例,看起来非常简单,但它的价值在于把“知”从业务逻辑代码里抽出来了。
python复制# knowledge/order_rules.py
from dataclasses import dataclass
@dataclass
class OrderSnapshot:
order_id: str
status: str
paid: bool
paid_amount: float
shipped: bool
class OrderCancellationRule:
"""订单取消规则——这是'知'层,只做领域判断,不产生任何副作用"""
def can_cancel(self, order: OrderSnapshot) -> bool:
