一、 遗留系统的典型困境与DDD重构的契机
许多成长于业务快速发展期的互联网企业,其核心系统往往背负着沉重的历史包袱。以Keep的供应链系统(进销存系统)为例,该系统作为电商业务的基石,在多年快速迭代后,逐渐暴露出严重问题,甚至可能阻碍业务的进一步发展。
-
系统边界模糊与架构腐化:最初的系统缺乏系统性规划,代码规范缺失,导致系统内部边界日益模糊。Usecase(用例)的职责越来越不清晰,代码边界和事务范围混乱,各层之间(如控制层、服务层、数据访问层)职责不清,甚至出现了循环依赖,使得系统难以理解和维护。
-
核心业务痛点:库存不准与上下文缺失:对于供应链系统而言,库存准确性是生命线。然而,旧系统频繁出现超卖(售出超过库存)或少卖(有库存却无法销售)的情况。更严重的是,库存变更的日志记录不规范,上下文信息缺失,一旦出现库存问题,排查原因困难重重,库存数据与变更日志无法自证正确性。
-
新业务需求的倒逼:除了修复历史问题,业务还提出了诸如店铺库存自动分配、智能采购、库存准确率保障、履约率保障等提升运营效率的新要求。在原有混乱的架构上修修补补已无法满足未来需求,一场以领域驱动设计为指导思想的系统性重构势在必行。
二、 DDD核心战略:梳理限界上下文与统一语言
DDD的核心在于将复杂的业务领域进行拆分,通过建立清晰的“限界上下文”和“统一语言”,让技术架构真实反映业务本质。Keep的重构正是从业务场景和领域模型的梳理开始。
-
深入业务,梳理核心场景:重构的第一步是跳出代码,深入业务。团队需要与领域专家(业务人员)紧密协作,梳理出所有与“库存”相关的业务场景,例如:采购入库、销售出库、退货入库、调拨、盘点损益、活动预占等。每个场景都涉及库存的变更,但变更的规则、影响的范围和后续流程各不相同。
-
划定限界上下文:在厘清场景后,需要划定“限界上下文”。这是DDD中一个关键的战略设计模式,用于定义模型应用的边界。例如,“库存上下文”可能专注于实物库存的准确记录和变更;“采购上下文”则关心采购订单的执行和供应商协同;“销售上下文”关注订单的创建和履行。明确边界后,库存的核心模型就不会被采购或销售的复杂逻辑所污染。
-
定义清晰的库存领域模型:在“库存上下文”内,需要建立精确、无歧义的领域模型。这不仅仅是数据库表结构,而是反映业务概念的对象及其行为。例如,区分“占用库存”(已售卖未出库)、“可用库存”(实物库存-占用库存)、“实物库存”、“在途库存”(已采购未入库)和“冻结库存”(因秒杀等活动预占)等模型。统一的模型语言确保了开发人员、产品经理和业务人员在同一频道上对话,从根本上减少了误解。
-

三、 DDD战术与架构落地:保障模型纯粹性与一致性
战略设计划分了边界,战术设计则关乎如何在边界内构建健壮的、可维护的软件。Keep的重构采用了多种架构模式来保护核心领域模型的纯粹性。
-
六边形架构(端口与适配器):采用六边形架构的核心目的是保证领域模型的稳定性。该架构将领域模型置于核心,外部通过“端口”(接口)与内部交互。无论是Web请求、消息队列消费还是数据库访问,都是通过“适配器”来接入。这样,领域层不依赖任何外部框架(如Spring、MyBatis)或基础设施(如数据库、缓存),从而保持核心业务逻辑的独立和可测试性。
-
CQRS(命令查询职责分离)模式:为了防止复杂的查询需求(如多表关联、聚合报表)腐化或影响核心领域模型(命令模型),引入了CQRS模式。将“命令”(增删改,引发状态变更)和“查询”(仅读取数据)的模型和路径分离。命令端严格遵循领域模型处理业务逻辑并发布领域事件;查询端则可以根据展示需求自由构建读模型,甚至使用不同的数据库(如Elasticsearch),从而兼顾模型的纯粹性与查询性能。
-
防腐层(ACL)与事件驱动:当“库存上下文”需要与“采购上下文”等其他限界上下文交互时,不能直接依赖对方的内部模型。通过建立“防腐层”,将外部上下文的信息转换成本上下文内的值对象。例如,采购上下文通过防腐层将库存上下文中的“仓库”信息映射为自己内部的“仓库值对象”。同时,采用事件驱动架构,库存变更后发布“库存已占用”、“库存已释放”等领域事件,其他上下文订阅这些事件并作出反应,实现了松耦合的集成。
四、 技术实现与重构收益:从混乱到清晰的价值体现
理论需要结合实践,DDD的落地离不开具体的技术选型和工程实践。Keep选择了COLA等架构框架来辅助落地,并特别关注了库存变更场景下的事务一致性难题。
-
EventStore解决分布式一致性:在库存变更场景中,一个业务操作(如销售出库)可能涉及上层业务单据(销售单)和底层核心单据(出库单)的状态联动。为了保证跨上下文、跨服务的数据最终一致性,团队采用了EventStore(事件存储)方案。将库存变更作为一个领域事件持久化到事件存储中,然后由订阅该事件的其他服务来更新自身状态。通过这种事件溯源的方式,任何状态变更都有迹可循,为问题排查和业务审计提供了完整依据。
-
单元测试保障重构质量:在持续集成开发中,单元测试是保障重构质量、提升开发效率的关键。团队为核心领域模型(Domain)和核心业务接口场景(CmdExe)添加了单元测试。引入Mockito库来模拟外部依赖,使用H2内存数据库避免测试污染,确保每次代码提交都能快速验证核心逻辑的正确性。
-
显著的业务与技术收益:此次DDD重构带来了多方面的收益:首先,最根本的是解决了库存不准的历史顽疾,为销售库存校准提供了准确基准;其次,清晰的代码结构和限界上下文使得功能扩展更加方便,例如快速对接财务系统;再次,问题定位速度极大提升,库存变更的上下文清晰可查;最后,沉淀出了通用的事件组件和一套成熟的DDD最佳实践,为后续库存系统、售后系统等重构提供了可复用的方法论和组件,实现了从“救火”到“治本”的转变。
-
