• home > theory > engineering > model >

    领域驱动设计与职责驱动设计

    Author:zhoulujun Date:

    OO分析设计的核心原则之一,就是软件系统中的所有元素都必须具有高度相关的职责。分析模型的首要设计原则就是职责驱动设计。前端开发DDD有时候很不适合,职责驱动设计在功能复杂的系统架构设计中可带来不少的帮。

    关于DDD,可以看之前装载的领域驱动设计之领域模型—DDD领域驱动设计基本理论知识》,这个博主总结的很不错。

    在面向对象编程中,有一种设计模式叫职责驱动设计(Responsibility-Driven Design,简称 RDD),最典型的就是“客户端-服务端”模型。职责驱动设计于 1990 年构想,是从将对象视为[数据+算法]到将对象视为[角色+职责]的转变。

    OO分析设计的核心原则之一,就是软件系统中的所有元素都必须具有高度相关的职责,也就是说,软件系统中所有的模块、包、对象类,都应当拥有一个清晰的职责,并且与它相关的所有元素(即模块中的所有包、包中的所有对象类、对象类中的所有属性和行为)都必须与这个职责具有高度的相关性。因此,分析模型的首要设计原则就是职责驱动设计(Responsibility-Drive Design)。

    依据职责驱动设计的原则,我们在分析系统的时候,必须为每一个模块、包、对象类,特别是对象类,定义一个明确的职责。在这个原则下,我们最核心的问题是,我们在定义每一个对象类时都应当为其定义职责,在定义它的行为和属性时,都应当与其职责高度相关

    如何进行职责驱动设计呢?

    大师Craig Larman在他的经典著作《UML和模式应用》中,提出了GRASP软件设计模式。GRASP(General Responsibility Assignment Software Patterns),翻译过来就是通用职责分配设计模式,它包括

    1. 创建者(Creator):将创建某个对象的职责分配给与该对象有密切关系的类,或者将对象创建的职责分配给其数据或行为的拥有者。如果一个类与另一个类有紧密的联系,且通常一起使用,那么由这个类来创建另一个类的实例是合适的。

      • 核心问题就是:谁应该负责创建一个对象创建者模式指定哪个对象应该负责创建其他对象的实例,如让一个类A创建另一个类B的实例。它决定了如何创建这些对象,比如使用工厂方法和抽象工厂。

      • 应用场景:当创建过程比较复杂,或者需要根据不同的条件创建不同的对象时。

    2. 控制器(Controller):将处理系统事件或消息的职责分配给一个专门的类。

      1. 核心问题:谁处理系统事件?将处理系统事件(如用户输入)的职责分配给代表整个系统、用例场景或逻辑功能区域的非UI类。这有助于实现关注点分离,使UI层更简单,且不包含业务逻辑。

      2. 应用场景:当系统需要响应来自多个来源的事件或消息时。主要用于将职责进行分配,比如常见的 MVC 架构模式中的控制器。

    3. 信息专家(Information Expert):将某个职责分配给拥有该职责所需信息的类。

      1. 核心问题:哪个类应该拥有执行特定任务所需的信息?将处理特定领域信息的所有操作委托给负责维护该信息的类。信息专家是最了解其数据的类,因此可以最有效地执行与该数据相关的任务。

      2. 应用场景:当某个职责需要访问多个类中的数据时。

    4. 低耦合(Low Coupling):降低类之间的依赖关系,使它们之间相互联系尽可能少。

      1. 核心问题:如何减少类之间的依赖关系?设计系统时尽量减少类之间的直接联系,通过引入中介者、接口或抽象类等机制来实现。低耦合有助于提高系统的灵活性和可维护性。

      2. 应用场景:当需要提高代码的可重用性时。

    5. 高内聚(High Cohesion):将功能相关的代码放在同一个类中,提高类的内聚性。

      1. 核心问题:如何确保类内部的元素协同工作以实现单一、明确的目的?将相关的行为和数据封装在一个类中,使其具有明确的职责和边界。高内聚有助于提高代码的可读性和可复用性。

      2. 应用场景:当需要提高代码的可重用性时。

    6. 多态(Polymorphism):使用继承和重写机制,使不同类型的对象可以响应相同的请求。

      1. 核心问题:如何处理具有相似特征但行为不同的实体?使用继承和多态来表示不同类型的实体,通过公共接口暴露其行为,从而实现代码的解耦和复用。

      2. 应用场景:当需要处理不同类型的对象时。用于表示具有不同行为的相关类,使用抽象而不是特定的具体实现。

    7. 纯虚构(Pure Fabrication):将一些相关的职责分配给一个虚构的类,以便提高代码的内聚性和可维护性。

      1. 核心问题:当领域模型中没有自然适合执行某些任务的类时,该怎么办?解决方案:创建一个类,它不代表实际的概念或实体,而是专门为实现特定功能而设计。这种类通常是服务类或工具类。

      2. 应用场景:当需要将一些相关的职责从其他类中分离出来时。为了保持良好的耦合和内聚,捏造业务上不存在的对象来承担职责。

    8. 间接性(Indirection)::通过中间类来实现两个类之间的通信,降低耦合性。

      1. 核心问题:当领域模型中没有自然适合执行某些任务的类时,该怎么办?创建一个类,它不代表实际的概念或实体,而是专门为实现特定功能而设计。这种类通常是服务类或工具类。

      2. 应用场景:当两个类之间需要频繁通信时。

    9. 防止变异(Protected Variations):将容易发生变化的代码封装在一个独立的类中,以降低代码的维护成本。核心就是封装,将细节封装在内部。如果内部表示或行为发生了变化,保持其公共接口的不变。

      1. 核心问题:如何设计系统以使其能够适应需求变化而不产生副作用?通过封装、抽象和模块化等手段,将可能发生变化的部分隔离起来,使其对系统其他部分的影响最小化。这有助于提高系统的稳定性和可扩展性。

      2. 应用场景:当代码需要经常修改时。

    GRASP这九部分,与其说是模式,不如说是原则更加准确。整个 GRASP 的核心是如何防止变异(变化)

    c84098bf0f4de993cb12adf3ba900a45.webp

    更新详细的推荐阅读:

    架构必修:领域边界划分方法 -- 职责驱动设计 (RDD) https://xie.infoq.cn/article/0f3eab53ac4228d769909425a

    其实,RDD 本身的设计具备更多的角色,包括服务提供商、接口、信息持有人、控制器、协调员、结构师;也具备更多的职责分配原则和模式,通常包括:

    • 单点原则:将信息保存在一个地方

    • 保持较小的职责,比如“得墨忒耳定律(Law of Demeter)-最少的知识原理”

    • 包装相关的操作,比如“Whole Value Object”

    • 接口隔离原则:仅使用需要的内容

    • 一致的职责,比如“单一职责原则”

    其实,这些看实际项目的运用……


    领域驱动设计 VS 职责驱动设计

    • 领域驱动设计(Domain-Driven Design,简称 DDD):从业务领域的角度来对系统进行领域划分和建模。

    • 职责驱动设计(Responsibility-Driven Design,简称 RDD):从系统内部的角度来进行职责划分、模块拆分以及协作方式。

    领域驱动设计(DDD)用于业务领域的划分,在业务复杂的系统架构设计中比较实用。比如电商领域的商品、买家/卖家、订单、优惠券、风控等各个领域的划分。

    DDD领域驱动设计强调业务领域是最重要的,数据不应该成为主导,而技术必须服从于业务需求

    DDD是什么?DDD主要用于解决业务逻辑复杂的软件系统的设计问题,是对大量复杂软件系统开发实践经验的一种总结。主要难度在于业务复杂,而非技术难度大

    DDD软件开发架构思想以数据为中心的传统软件开发

    推荐阅读:领域驱动设计(DDD)之程序员视角-北理-金旭亮 https://zhuanlan.zhihu.com/p/679540368

    但在前端领域中,不同的业务领域通常会通过不同的页面、组件等方式出现,比如商品页面、订单页面、优惠券页面等。因此领域驱动设计(DDD)很少在前端开发中使用,或者可以说在前端领域的应用与前端组件化思想比较相似。

    至于职责驱动设计(RDD),它更倾向从角色和职责的角度来定义和划分模块,与前端的公共组件、工具库、MCV/MVVM 设计、功能模块划分等类似。职责驱动设计(RDD)在功能复杂的系统架构设计中可带来不少的帮助,比如上面提到的在线文档中的各个功能模块的设计中,可以通过对系统进行职责划分以及定义模块之间的边界和协作方式,从而清晰地模块的功能。

    这两种设计模式并不是互斥的,我们可以配合一起使用,比如:

    1. 我们可以将与业务逻辑关系密切的功能按照业务领域进行划分和建模,比如电商网站中的购物车组件、商品组件等;

    2. 对于与前端实现(视图渲染逻辑、与服务端交互逻辑、与用户交互逻辑)相关的功能,我们可以在具体的系统搭建过程中对这些功能进行职责分配和模块划分。

    当我们对模块进行划分之后,还需要考虑模块的设计、模块间的依赖关系和通信等问题。其中,最常见的便是如何解决模块间的依赖耦合的问题。




    参考文章:

    在线文档的网络层开发思考--职责驱动设计 https://godbasin.github.io/2021/01/23/network-design-responsibility-driven-design/



    转载本站文章《领域驱动设计与职责驱动设计》,
    请注明出处:https://www.zhoulujun.cn/html/theory/engineering/model/9094.html