文字写完以后,突然想起来很久以前在JavaEye上看的关于贫血模型、富血模型、充血模型的讨论。最后的结论大概与持久化无关的业务逻辑应该放在模型中实现,而与持久化有关的逻辑要放到Service中实现。看这几多文字,其实也就是说这么个意思,不过多说一些。闲话休提,正文开始。
在大结构上系统描述为上面的图形。关键元素是 领域模型、 领域服务、 基础服务。能够精准的反应业务的模型,和围绕模型的有效的服务,是软件团队在某一行业领域生产成果价值的两个很重要的体现。领域服务作为系统的核心逻辑之一,一方面,承担着完成所有与技术环境相关的业务逻辑的实现,另外一方面,作为领域模型和技术环境的缓冲,保证了领域模型的 普适、独立。
作为独立的价值体现,建模的结果需要有相当高的普适性。概念模型因为和实现无关所以具有先天的普适性,但到了实现层次,保证模型的独立性就成了一个需要专门考虑的问题。通常的做法是对于一个业务逻辑,判断它是否与环境相关,如果不是,那么放在模型中实现,如果是,则放在领域服务中实现。这样保证领域模型的普适性,得到了可移植的最大价值。领域服务作为领域模型和基础服务之间的一个缓冲层次,隔离领域模型和环境以及上下文。丢失领域模型的普适性可能是技术架构的问题导致的,也可能是建模过程的问题导致的。
在技术架构方面,一方面,我们要使模型尽可能的普适,那么就要求模型尽可能的少依赖环境。严格说来,模型应该完全不依赖与环境。例如我们将行为主体建模成为一个User模型,他有名称,有密码,但很明显这一切与环境无关。不论你的技术架构是开源的技术栈,或者是公司自己的技术积累,这个模型应该是都是成立的。在主流的以开源技术栈上实现的应用中,一般都会有DAO(Data Access Object),Service等对象在POJO类周围,完成领域服务,同时让POJO模型仅从事与环境无关的业务实现。由此看来,所谓不依赖,和非侵入性有很大关系。在Hibernate中,以XML来描述映射关系元数据,保证了模型不被侵入(Hibernate3.2以后加入了在Class上面用Annotation配置映射源数据的方式,但因为Annotation本身是程序元数据而不是程序的常规内容,不影响业务逻辑实现,所以也可以看做是没有侵入)。由此也可以看出另外一个方面,系统的技术架构为了能够实现尽可能多的“通用服务”,需要在模型上加入许多的限制,这种限制在Hibernate中体现为:POJO要符合JPA规范的要求,同时,严格一点,应该符合JavaBeans规范。但是我们也看到,模型遵循这些规范并没有给自己带来侵入和对环境的依赖。所以我认为,实现通用服务所要求的对模型的限制,完全可以仅仅是规范的形式,而不是直接到实现层次。
在建模方面,准确的分辨一个业务逻辑单元是否依赖环境,将那些与环境无关的业务逻辑放在模型实现中,而将那些与环境紧密相连的业务逻辑放在服务中。原因也可以从两个方面来说明,一个方面是许多业务的实现过程,本身就是领域对象所应该包含逻辑的表达。例如给一个用户组增加用户,我们会建模成为Group.add(User)。与服务无关。是为职责所定,不能乱放。另外一个方面,如前所述,领域模型与环境无关,而领域服务则依赖于环境,于是领域模型可以普适,轻易的移植,而领域服务则不然,这也将使我们实现业务逻辑的时候,应该默认的选择领域对象,用业务逻辑的实现来使领域模型更加富有内涵,将使我们在不同的系统中达到最大限度的重用,包括代码重用和业务逻辑重用。所以,良好的区分模型和服务的职责和边界,是业务系统设计要解决的最重要的问题之一。
另外一个需要说明白的问题是依赖方向。Java企业及应用开发发展这么多年,追求系统各部分之间的松耦合这个方向一直没有变。松耦合带来的好处无需赘言。对于具体的实现方式,或者说软件设计、实现中应该考虑、注意的问题,有很多的开源框架做了深入的探讨。通用的模式是表现、领域服务、模型、基础服务各个部分按照纵向排列,任意二者之间最多只能有一个方向的依赖,而且不相邻层次之间不允许发生直接依赖关系。表现层依赖于模型和领域服务,领域服务依赖于模型和基础服务,模型不对外界环境发生依赖,基础服务也不可能对其上面的层次发生依赖。系统模块确定的对外接口和单向依赖使得各部分之间的耦合程度降到最低,一个部分内部的变化不会影响到其他部分,对外接口的调整也仅影响到系统架构上更高的层次,由此提高系统的可扩展性、可维护性。从技术角度上讲,一些细节的实现需要模型的实现者依赖环境。例如在实现延迟加载这个细节的时候,模型中必须存在对能够获取到数据的对象的引用,于是就造成了对环境的依赖。但是,这个情况的发生,应该在模型类的可见级别控制范围内,或者从规范以及建议层面禁止直接调用。