充血模型中的职责分配

     关于充血模型和贫血模型的讨论iteye上已经很多了,不是谁好谁坏的问题,都有适用的场景,业务复杂的当然可以使用充血模型,那些讨论几乎都是浪费口舌,也没说到点子上,讨论的话题应该是如何将业务逻辑合理分配到协作的多个对象上。这里只关注充血模型中的职责分配,因为在设计开发中很容易在这方面出现问题,在充血模型中,一种最极端最常见的情况是将过多的业务逻辑不分青红皂白都放在领域对象中(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 -》domain object -》 持久层(集成层、基础设施层) 。

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

  从上面的层次结构可以看出,业务逻辑可以分布在application service、domain service和domain object这三类之中,显然将过多的业务逻辑分配在domain object是很可能出现问题的。

 

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

    哪些业务逻辑放在领域对象中?哪些放在domain service中?哪些放在application service中?

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

  还有一个是基础设施服务/公共服务,例如第三方的集成服务、数据访问/数据持久服务、短信基础服务。

      

  

    

     关于充血模型和贫血模型的讨论iteye上已经很多了,不是谁好谁坏的问题,都有适用的场景,业务复杂的当然可以使用充血模型,那些讨论几乎都是浪费口舌,也没说到点子上,讨论的话题应该是如何将业务逻辑合理分配到协作的多个对象上。这里只关注充血模型中的职责分配,因为在设计开发中很容易在这方面出现问题,在充血模型中,一种最极端最常见的情况是将过多的业务逻辑不分青红皂白都放在领域对象中(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 -》domain object -》 持久层(集成层、基础设施层) 。   access service提供面向客户端的接入服务,主要是接入层的请求路由、控制逻辑以及和具体客户端,用户类型相关的特殊逻辑,处理访问的认证鉴权、请求&响应的转化/格式化/消息打包解包,将访问路由到对应的application service,它尽量不要包含业务逻辑。在spring mvc中,access service体现为dispatch servlet+controller+过滤拦截器。   从上面的层次结构可以看出,业务逻辑可以分布在application service、domain service和domain object这三类之中,显然将过多的业务逻辑分配在domain object是很可能出现问题的。     对业务逻辑的分配,如果我们能明确其分配的具体原则,划分的边界,就可以做到合理分配。先前提到的OO原则是比较高层的通用的原则。     哪些业务逻辑放在领域对象中?哪些放在domain service中?哪些放在application service中?
  • 显然领域对象负责的是领域逻辑,退回到领域逻辑的概念定义上来,领域逻辑应该是该领域对象专有的逻辑,它是不随应用、用例、客户端而改变的,和应用、用例、客户端无关。例如对某领域中的身份证领域对象,其身份证的验证规则是不变的,身份证总是18位,不管是在哪个应用中,还是哪个用例中,该规则都不变,那很显然这个规则就是身份证领域对象的领域逻辑。再比喻,在银行账号中,如果规定取款金额必须小于余额,这个逻辑不管你的取款流程是什么,不管是银行的哪个应用,这个逻辑都保持不变,那这个就是账号的领域逻辑。可见这也是符合信息专家原则的。如何判断逻辑是不是该领域对象专有的,去问领域专家,他对业务是最清楚的,领域模型其实应该是他负责建模的。
  • 领域服务(domain service),这个也类似领域对象,也就是它的逻辑是不随应用、用例、客户端而改变,是该领域专有的,只是其领域逻辑要跨多个领域对象,例如银行转账,涉及到两个领域对象。它一般是起协调或控制作用,对应设计模式中的facade模式。领域服务和领域对象比较稳定,可以在多个应用中重用。
  • 应用服务(application service),划分好了上面两个的职责,这个就比较简单了,剩下的逻辑就基本属于应用逻辑(不管展示层逻辑)。相比而言,应用逻辑(应用服务)是最不稳定的,因为它是每个应用专有的,一般不能在其他应用中重用。例如某用例中,需要发送短信通知,通常这个不属于领域对象和领域服务,只能是应用逻辑。应用服务是领域服务和领域对象的宿主环境和运行容器,也就是应用服务包装/调用领域服务和领域对象。对复杂的应用服务,也可采用facade模式。

猜你喜欢

转载自wanshi.iteye.com/blog/1921055
今日推荐