欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

充血模型中的职责分配

程序员文章站 2022-07-03 22:29:21
...

关于充血模型和贫血模型的讨论iteye上已经很多了,不是谁好谁坏的问题,都有适用的场景,业务复杂的当然可以使用充血模型,那些讨论几乎都是浪费口舌,也没说到点子上,讨论的话题应该是如何将业务逻辑合理分配到协作的多个对象上。和充血模型相关的是DDD领域驱动设计,其分层结构如下。

充血模型中的职责分配
            
    
    博客分类: 分析设计  
  这里只关注充血模型中的职责分配,因为在设计开发中很容易在这方面出现问题,在充血模型中,一种最极端最常见的情况是将过多的业务逻辑不分青红皂白都放在领域对象中(Domain object),导致领域对象太臃肿,违反单一职责原则,很可能引起领域对象不稳定,它本来应该是比较稳定的,可在多个应用中重用。

 

  在Java spring应用中之所以流行贫血模型的主要原因是领域对象(实体对象)一般是在spring容器之外创建的,例如主动new一个领域对象,或dao方法返回领域对象,或反序列化生成。通过这些方式创建的领域对象,其包含的依赖对象(如一些服务)是无法通过声明(例如通过@Autowired注解来声明或在xml配置中通过ref来声明)来自动注入到领域对象中的,需要领域对象自己创建依赖的对象实例,比较麻烦。这其实是误解,从spring2.0开始就支持将依赖的spring bean注入到领域对象中,即使领域对象是在spring容器之外创建的。Spring使用Configurable注解(注意不是Configuration注解)和AspectJ aop等来实现注入依赖对象到领域对象中,成为充血模型,这样充血领域对象就能和这些依赖的对象进行交互和协作来实现其领域逻辑,而不是贫血模型中将领域逻辑移到其他服务中。

 

    OO设计原则(如单一职责等)和Craig Larman的GRASP(如信息专家)都是用来指导职责分配的,怎么这些在充血模型中就失效了,其实问题最终原因还是对这些原则没有真正掌握。

    以处理用例(use case)中客户请求的层次结构为例,采用充血模型后,通常的交互层次是:client -》access service(接入层服务,面向各种客户端,提供多种形式的接入接口,如rpc、http/rest接口) - 》应用层服务(application service) -》领域层服务(domain service or domain object)  -》 集成层(数据持久、基础设施服务) 。

  access service提供面向客户端的接入服务,主要是接入层的请求路由、控制逻辑以及和具体客户端,用户类型相关的特殊逻辑,处理访问的认证鉴权、请求&响应的转化/格式化/消息打包解包,将访问路由到对应的application service,它尽量不要包含业务逻辑。在spring mvc中,access service体现为dispatch servlet+过滤拦截器。

  从上面的描述可以看出,业务逻辑可以分布在应用层服务(application service)、领域层服务(domain service和domain object)、集成层/基础设施服务这3类之中,显然将过多的业务逻辑分配在领域对象(domain object)是不合理的。

 

  对业务逻辑的分配,如果我们能明确其分配的具体原则,划分的边界,就可以做到合理分配。先前提到的OO原则是比较高层的通用的原则。

  从服务化的思想看,DDD分层架构也是对服务类型的分类:应用服务application service、领域层服务(领域对象、领域服务domain service)、基础设施服务。

    一个用例use case中包含很多业务逻辑和业务规则,哪些业务逻辑和业务规则放在领域层服务中,哪些放在应用服务中?哪些在基础设施服务中。也就是业务逻辑分配(划分)的标准是什么?这其中让很多人困惑,不好把握的是领域层(领域逻辑)和应用服务(应用逻辑)的划分标准,很多人只是口头上知道领域逻辑和应用逻辑,但要他具体讲解领域逻辑和应用逻辑如何划分,划分的标准是怎样的,几乎很少有人能说明白说透彻,模棱两可,没有可操作性。下面讲解服务的分类以及领域逻辑和应用逻辑的具有可操作性的区别和划分标准,区别见下面的黑体字所描述的部分,知道了可操作性的区别,划分标准也就真的清楚了。

     

  • 显然领域对象负责的是领域逻辑,退回到领域逻辑的概念定义上来,领域逻辑应该是该领域对象专有的逻辑,它是不随应用、用例、客户端而改变的,和应用、用例、客户端无关。例如对某领域中的身份证领域对象,其身份证的验证规则是不变的,身份证总是18位,不管是在哪个应用中,还是哪个用例中,该规则都不变,那很显然这个规则就是身份证领域对象的领域逻辑。再比喻,在银行账号中,如果规定取款金额必须小于余额,这个逻辑不管你的取款流程是什么,不管是银行的哪个应用,这个逻辑都保持不变,那这个就是账号的领域逻辑。可见这也是符合信息专家原则的。如何判断逻辑是不是该领域对象专有的,去问领域专家,他对业务是最清楚的,领域模型其实应该是他负责建模的。
  • 领域服务(domain service),这个也类似领域对象,也就是它的领域逻辑是不随应用、用例、客户端而改变,是该领域专有的,只是其领域逻辑要跨多个领域对象,例如银行转账,涉及到两个领域对象。它一般是起协调或控制作用,对应设计模式中的facade模式。
  • 应用服务(application service),划分好了上面两个的职责,这个就比较简单了,剩下的逻辑就基本属于应用逻辑(不管展示层逻辑)。相比而言,应用逻辑(应用服务)是最不稳定的,因为它是每个应用专有的,一般不能在其他应用中重用。例如某用例中,需要发送短信通知,通常这个不属于领域对象和领域服务,只能是应用逻辑。对复杂的应用服务,也可采用facade模式对领域层服务进行服务编排。
  • 基础设施服务/公共服务,例如集成服务、中间件、治理层、数据访问/数据持久服务、消息服务、短信基础服务。

   综上所述,是否公共共享重用、相对稳定不变才是划分业务逻辑到领域层和应用层的标准,此外领域层也可能较薄,应用层可能厚。

   领域层服务比较稳定,可以在多个应用中重用,也就是领域对象和领域服务可被多个上层应用的应用服务共享,应用服务是领域服务和领域对象的宿主环境和运行容器,也就是应用服务包装/调用领域服务和领域对象。  如果我们用服务化框架将领域服务和领域对象的能力开放出去,就是我们通常称呼的各种XX中心,例如订单中心、用户中心。 

     

      

  • 充血模型中的职责分配
            
    
    博客分类: 分析设计  
  • 大小: 65.4 KB