上一篇文章介绍了一线开发人员的关注点:DDD的战术落地。本篇文章则以一个高层视角,介绍下架构人员更关注的DDD战略设计相关问题。
DDD的战略设计聚焦于领域的整体架构规划,解决 “做什么” 的问题。而想要深入回答这个问题,则一定要对业务有深入的了解。从业务角度出发,建立业务的领域模型,合理的划分领域的业务边界,同时建立限界上下文,这些构成了DDD战略设计阶段的关键任务。有效的战略设计才能打造出于业务紧密结合,具有高可维护性和高扩展性的软件系统,应对业务的不断变化和发展。
一、领域划分
一个领域可以理解为一个问题域(系统的核心业务)。只要是同一个领域,那么问题域就相同。例如:一个电商领域的问题无非是订单、商品、物流、支付等,绝对不会涉及到出行、政法等。
1.1、领域划分方法介绍
除了基于业务功能、业务流程和业务组织架构的划分方式外,在实际项目中,还有一些专业且高效的领域划分方法:
- 四色建模法:这是一种通过颜色标记不同类型对象来梳理业务模型的方法。其中,黄色代表 “主角”(参与业务的核心实体,如电商中的 “用户”“商品”);粉色代表 “描述”(对主角的补充信息,如 “商品详情”“用户地址”);蓝色代表 “时刻 / 时段”(业务事件发生的时间节点或周期,如 “订单创建时间”“促销活动时段”);绿色代表 “角色”(主角在特定场景下的身份,如 “买家”“卖家”)。通过对业务中各类对象的颜色标记和关系梳理,能清晰识别核心实体及关联,为领域划分提供依据。
- 事件风暴法:以业务事件为核心,通过收集领域内的所有事件(如 “订单创建”“商品出库”),并梳理事件的触发条件、涉及的实体和角色,来发现业务的核心流程和关键实体。在这个过程中,团队成员共同参与,围绕事件进行讨论,逐步理清业务脉络,进而划分出不同的领域。例如,在电商业务中,围绕 “订单支付成功”“物流发货” 等事件,能明确订单、支付、物流等相关业务的范围和边界。
- 限界纸笔法:这是一种简单直观的方法,团队成员使用纸笔,将认为属于同一领域的业务功能、实体、流程等内容写在纸上,然后通过讨论和调整,确定领域的范围和边界。这种方法操作灵活,适合在领域划分初期快速形成初步方案。比如,在项目初期,团队成员分别写下自己认为与订单相关的业务内容,然后汇总讨论,确定订单领域的大致范围。
- 领域故事法:通过讲述领域内的业务故事,提炼出关键的业务场景、角色、行为和实体,进而划分领域。每个故事都包含 “谁(角色)在什么场景下做了什么(行为),涉及什么(实体)”。例如,在金融贷款领域,“借款人提交贷款申请,审核人员审核申请资料,通过后发放贷款” 这个故事,可提炼出借款人、审核人员等角色,贷款申请、贷款发放等行为,贷款申请资料等实体,据此可划分出贷款申请领域、贷款审核领域等。
- 用例分析法:先识别系统的用例(用户与系统的交互场景),再根据用例的关联性划分领域。用例关联性高(如共享实体、流程衔接紧密)的用例可归为同一领域。在在线教育系统中,“学生选课”“学生提交作业”“教师批改作业” 这些用例关联性强,都围绕学生学习和教师教学展开,可划分为教学管理领域;而 “系统管理员添加课程”“系统管理员维护用户信息” 等用例可划分为系统管理领域。
- 价值流分析法:聚焦业务中为客户创造价值的流程,将完整的价值流及其相关活动划分为一个领域。在汽车制造企业的系统中,从 “零部件采购”“汽车组装”“质量检测” 到 “汽车交付” 的价值流,可划分为汽车生产领域,因为这些环节共同为客户创造了 “获得合格汽车” 的价值。
1.2、领域划分步骤示例
以一个电商平台项目为例,具体步骤如下:
步骤一:准备工作
组建包括业务专家、开发人员、产品经理在内的项目团队,收集电商平台相关的业务资料,如业务手册、现有系统文档等。
步骤二:应用事件风暴法收集事件
团队成员共同列举电商业务中的所有事件,如 “用户注册成功”“商品上架”“用户下单”“订单支付”“商品发货”“用户确认收货” 等。将这些事件记录在便签上,贴在白板上。
步骤三:结合四色建模法梳理实体和关系
针对每个事件,确定涉及的主角(黄色),如 “用户”“商品”“订单”;描述信息(粉色),如 “商品规格”“订单金额”;时刻(蓝色),如 “下单时间”“支付时间”;角色(绿色),如 “买家”“卖家”。并梳理它们之间的关系,例如 “用户(买家)” 创建 “订单”,“订单” 包含 “商品” 信息。
步骤四:初步划分领域
根据事件和实体的关联程度,将相关的事件、实体和流程归为同一领域。例如,“用户注册”“用户信息修改” 等事件及相关的 “用户” 实体,可划分为用户管理领域;“商品上架”“商品信息更新”“商品下架” 等事件及 “商品” 实体,划分为商品管理领域;“用户下单”“订单支付”“订单取消” 等事件及 “订单” 实体,划分为订单管理领域。
步骤五:验证和调整
组织业务专家和团队成员对初步划分的领域进行评审,检查是否存在领域边界模糊、职责交叉等问题。例如,检查 “商品库存变更” 这一事件,发现它既与商品管理相关(商品信息包含库存),又与订单管理相关(订单创建会导致库存减少)。经过讨论,确定库存管理作为商品管理领域的一部分,同时订单管理领域在创建订单时需调用商品管理领域的库存接口,以此明确两个领域的边界。最终确定电商平台的领域划分为用户管理领域、商品管理领域、订单管理领域、支付管理领域、物流管理领域等。
产出:领域划分清单(明确各领域名称及包含的业务范围)、领域关系图(展示各领域之间的关联和交互方式)。
1.3 注意事项
- 避免过度划分:划分过细会导致领域之间的交互过于复杂,增加系统的维护成本。例如,在一个简单的小型电商系统中,如果将商品管理领域进一步细分为商品名称管理、商品图片管理、商品价格管理等多个微小领域,会使得各领域之间的协调变得繁琐,开发和维护难度增大。
- 保持领域的稳定性:选择相对稳定的业务因素作为领域划分的依据,避免因业务的短期变化频繁调整领域划分。例如,在金融借贷系统中,基于贷款业务的核心流程和规则划分领域,这些流程和规则在一定时期内相对稳定,而不是根据市场推广活动的短期变化来划分领域,否则会导致系统架构频繁变动,影响系统的稳定性和可维护性。
- 考虑领域的扩展性:在进行领域划分时,要预见到业务未来可能的发展方向,为领域的扩展留出空间。例如,在一个社交媒体平台的设计中,除了当前已有的用户管理、内容发布管理等领域,考虑到未来可能开展电商业务,可预留相关的领域扩展点,以便在业务拓展时能够顺利将电商相关领域融入系统架构中,而不需要对现有架构进行大规模重构。
二、子域划分
2.1、子域分类
子域可以理解为是更细分的领域。而其根据业务的侧重点可以分为:核心子域、通用子域和支撑子域。
- 核心子域:是企业核心竞争力的体现,直接关系到业务的成功与否,具有独特性和创新性。例如,在电商平台中,订单交易和支付流程是核心子域,因为它直接决定了交易的达成和企业的营收,且不同电商平台的订单交易流程可能存在差异,是体现平台竞争力的关键。
- 支撑子域:为核心子域提供支持,是核心子域正常运行所必需的,但不具备核心竞争力,可根据业务需求进行定制开发。如电商平台中的商品库存管理子域,它支撑着订单交易的正常进行(确保有货可售),但多数电商平台的库存管理逻辑较为相似,不构成核心竞争力。
- 通用子域:是多个领域或子域都可能用到的通用功能模块,不具备业务独特性,通常可以采用成熟的解决方案或组件。例如,电商平台中的日志管理、权限认证子域,这些功能在很多系统中都需要,可直接使用现成的工具或框架实现。
2.2、子域划分步骤示例
继续以电商平台项目为例:
步骤一:明确领域范围
基于已划分的电商领域,如用户管理领域、商品管理领域、订单管理领域等,确定各领域的业务范围。
步骤二:识别核心业务功能
分析各领域中对电商平台核心竞争力起关键作用的业务功能。在订单管理领域中,订单的创建、支付流程、订单状态管理等功能直接影响用户的购物体验和交易成功率,是核心业务功能。
步骤三:划分核心子域
将核心业务功能所在的部分划分为核心子域。在电商平台中,订单管理领域中的订单交易子域(包含订单创建、支付、状态管理等)即为核心子域。
步骤四:划分支撑子域
识别为核心子域提供支持的业务功能,将其划分为支撑子域。商品管理领域中的商品库存子域为订单交易子域提供库存信息支持,属于支撑子域;用户管理领域中的用户信息子域为订单交易提供用户收货地址等信息,也属于支撑子域。
步骤五:划分通用子域
找出各领域中通用的功能模块,划分为通用子域。电商平台中的日志管理、权限认证等功能,在用户、商品、订单等多个领域都有使用,属于通用子域。
产出:子域划分清单(包含子域名称、所属领域、类型及业务范围)、子域关系图(展示子域之间的支撑关系)。
2.3、注意事项
- 避免核心子域过多:核心子域需要投入大量的资源进行开发和优化,如果核心子域过多,会导致资源分散,无法集中精力提升真正的核心竞争力。例如,一个电商平台如果将用户管理、商品管理、订单管理都列为核心子域,可能会导致每个子域都无法得到充分的资源支持,影响整体竞争力。
- 合理确定支撑子域的范围:支撑子域的范围不宜过大或过小,过大可能包含非必要功能,增加开发成本;过小可能无法为核心子域提供足够的支持。例如,商品库存子域如果范围过小,仅包含库存数量统计,而不包含库存预警功能,可能无法及时应对商品缺货问题,影响订单交易。
- 通用子域的选型要合适:在选择通用子域的解决方案时,要考虑其稳定性、兼容性和可扩展性,避免因选型不当影响系统的整体性能。例如,选择权限认证组件时,要确保其能与电商平台的技术架构兼容,且能满足平台对权限管理的需求。
- 避免核心子域与支撑子域混淆:核心子域与支撑子域的定位和资源投入不同,混淆两者会导致资源错配。若将支撑子域当作核心子域投入过多资源,会造成浪费;若将核心子域当作支撑子域对待,会影响业务竞争力。
三、通用语言的定义
为防止各个角色参与项目时出现鸡同鸭讲的情况,需要定义一套让项目参与人员都能够达成共识的语言。这套语言要能够正确的,简单的,清晰的表达业务。
3.1、通用语言定义步骤示例
以电商平台的订单管理领域为例:
步骤一:收集术语
组织业务人员、开发人员、测试人员等,收集订单管理领域中涉及的术语,如 “订单”“订单项”“下单”“支付”“发货”“收货”“订单状态” 等。
步骤二:明确术语含义
针对每个术语,由业务专家进行解释,确保团队成员理解一致。例如,“订单” 是指用户在电商平台购买商品后生成的交易凭证,包含商品信息、用户信息、支付信息等;“订单状态” 包括 “待支付”“已支付”“已发货”“已收货” 等。
步骤三:定义术语使用规则
确定术语在沟通、文档编写、代码开发中的使用规范。例如,在代码中统一使用 “Order” 表示 “订单”,在文档中描述订单状态变化时,必须使用定义好的 “待支付”“已支付” 等术语。
步骤四:编制通用语言手册
将定义好的术语、含义及使用规则整理成通用语言手册,分发给团队成员,并组织培训。
产出:通用语言手册(包含术语、定义、使用规则)。
3.2、注意事项
通用语言不能仅停留在文档层面,要在代码中有所体现,如类名、方法名、变量名等使用通用语言中的术语。例如,在代码中使用 “OrderStatus” 枚举类来表示订单状态,其枚举值为 “PENDING_PAYMENT”“PAID” 等,与通用语言中的术语对应,避免出现代码与业务术语脱节的情况。
四、限界上下文
限界上下文是 DDD 中用于界定领域模型边界的核心概念。它可以理解为一个 “语义边界”—— 在这个边界内,领域模型(包括实体、值对象、业务规则等)具有一致性,通用语言能够统一生效;而边界之外,同一术语可能有不同含义,模型也可能存在差异。简单来说,限界上下文就像一个 “容器”,把一套完整且自洽的业务逻辑和领域模型包裹起来,确保模型内部没有歧义,同时与其他 “容器”(其他限界上下文)通过明确规则交互。
例如,在电商系统中,“商品” 在 “商品管理上下文” 中强调规格、库存等属性,而在 “订单上下文” 中可能仅需名称、单价等信息 —— 这两个上下文通过各自的模型处理业务,再通过接口传递必要数据。
4.1、限界上下文定义步骤示例
以电商平台的订单管理领域为例:
步骤一:梳理业务流程
详细梳理订单管理的业务流程,如用户下单、订单审核、支付订单、订单发货、订单完成等。
步骤二:识别业务边界
根据业务流程,确定订单管理中不同业务活动的边界。例如,订单审核环节主要涉及订单信息的校验,与支付环节的业务逻辑和数据处理方式不同,存在明显的业务边界。
步骤三:确定限界上下文范围
将具有独立业务边界的业务活动及相关的实体、数据、业务规则等划分为一个限界上下文。在订单管理领域中,可划分出 “订单创建上下文”“订单支付上下文”“订单发货上下文” 等。其中,“订单支付上下文” 包含支付相关的实体(如支付记录)、业务规则(如支付方式、支付金额校验)等。
步骤四:定义上下文交互方式及映射关系
明确不同限界上下文之间的交互方式及映射关系。常见的映射关系包括:
例如,“订单支付上下文” 在支付成功后,通过事件通知 “订单发货上下文”,触发订单发货流程。
- 合作关系:如 “订单创建上下文” 与 “订单支付上下文” 紧密协作,需定义明确的交互协议,“订单创建上下文” 在订单创建完成后,向 “订单支付上下文” 传递订单信息。
- 防腐层:当 “订单支付上下文” 对接第三方支付系统时,设计防腐层,将第三方支付的接口封装为内部统一的接口,避免内部逻辑受外部系统影响。
产出:限界上下文清单(包含上下文名称、业务范围、交互方式及映射关系)、上下文交互图。
4.2、注意事项
- 保持上下文的独立性:每个限界上下文应具有独立的业务逻辑和数据模型,避免上下文之间的过度耦合。例如,在一个企业资源规划(ERP)系统中,财务模块和生产模块分别属于不同的限界上下文。财务模块专注于财务核算、报表生成等业务,生产模块负责生产计划制定、生产过程管理等业务。如果财务模块过度依赖生产模块的数据结构和业务逻辑,当生产模块进行业务调整或升级时,可能会对财务模块产生不必要的影响,甚至导致财务模块无法正常运行。
- 上下文之间的接口:上下文之间的交互接口要清晰、稳定,并且遵循一定的规范。这样可以降低上下文之间的依赖风险,提高系统的可维护性和扩展性。例如,在一个社交媒体平台和第三方广告投放平台的集成中,社交媒体平台作为一个限界上下文,向广告投放平台提供用户画像数据用于广告精准投放。双方需要明确数据接口的格式、数据传输频率、数据安全要求等规范。如果接口不明确,可能会导致数据传输错误、数据不一致等问题,影响广告投放效果和用户体验。
- 考虑业务变化对上下文的影响:随着业务的发展和变化,限界上下文可能需要进行调整和优化。例如,当电商平台开始拓展跨境业务时,原有的订单管理限界上下文可能需要考虑跨境订单的特殊业务规则,如海关报关、国际物流等,可能需要对订单管理限界上下文进行扩展或重新划分。企业应建立相应的机制,及时跟踪业务变化,评估其对限界上下文的影响,并采取相应的调整措施,确保系统能够持续适应业务的发展需求。
五、战略设计与战术设计的衔接
战略设计为战术设计提供基础框架,二者通过具体映射规则实现衔接,核心逻辑可分为五个关键维度:
- 限界上下文与代码模块的映射
- 映射逻辑:限界上下文的业务边界直接对应代码层面的模块划分,确保模块内业务逻辑的内聚性。
- 示例:电商 “订单支付上下文” 对应 “order-payment” 模块,集中存放支付逻辑、状态管理、第三方支付接口交互等代码。
- 交互规则:不同模块通过明确接口通信(如 “订单创建模块” 调用 “支付模块” 接口触发支付),保持低耦合。
- 核心子域业务规则与技术实现的转化
- 转化路径:核心子域的业务规则拆解为领域服务的具体方法,并通过聚合根保证数据一致性。
- 示例:
- 战略层 “支付金额校验规则”→战术层PaymentService的validateAmount()方法(校验金额非负、与订单金额匹配);
- 通过聚合根Order同步状态(支付成功后调用updateStatus("PAID"),并触发物流通知)。
- 支撑子域与技术实现的适配
- 适配逻辑:支撑子域为核心子域提供基础支持,技术实现以满足核心需求为目标,可适度复用现有组件。
- 示例:
- 战略层 “商品库存子域”(支撑订单交易)→战术层InventoryService的deductStock()方法(扣减库存并返回可用量);
- 与核心子域交互(订单创建时调用checkStock()接口,确保有货可售)。
- 设计原则:优先保证稳定性(如库存数据采用事务控制防超卖),无需过度设计扩展性。
- 通用子域与技术选型的匹配
- 选型逻辑:通用子域采用成熟解决方案,无需重复开发,聚焦集成适配。
- 示例:
- 战略层 “权限认证子域”→战术层直接集成 Spring Security 框架;
- 战略层 “日志管理子域”→战术层使用 Logback 组件,通过LogService封装统一打印接口。
- 集成规则:通过适配层隔离第三方组件(如封装AuthAdapter适配权限框架,避免核心代码依赖具体组件)。
- 通用语言在战术设计中的贯穿
- 应用范围:实体、值对象、领域服务的命名需严格遵循通用语言定义。
- 示例:
- 订单实体命名为Order(对应通用语言 “订单”);
- 订单状态枚举OrderStatus的 “PENDING_PAYMENT”“PAID” 对应业务术语 “待支付”“已支付”;
- 领域服务方法createOrder()“创建订单”、updateOrderStatus()“更新订单状态” 直接映射业务动作。
六、总结
DDD 战略设计是软件开发中至关重要的环节,领域划分通过多种方法明确了系统的业务边界;子域划分区分了核心、支撑和通用子域,为资源分配提供依据;通用语言确保了团队沟通的一致性;限界上下文(作为语义边界)明确了业务模块的边界和交互关系。同时,战略设计需与战术设计有效衔接,为技术选型和团队组织提供指导,并建立迭代与演进机制以适应业务变化。
在实际项目中,只有将这些环节有机结合,严格遵循注意事项,避免常见误区,才能充分发挥 DDD 战略设计的优势,构建出贴合业务需求、具有良好扩展性和稳定性的软件系统,支撑业务的持续发展。
全部评论