传统三层架构和领域模型三层架构

在过去的开发中,我们经历了 Servlet、JSP、三层架构等开发模式。jsp 就是在 html 里面写 java 代码,servlet 就是在 java 里面写 html 代码,这两种模式下代码的耦合度太高了,导致代码臃肿、需求修改和架构优化捉襟见肘。三层架构模式是目前广泛应用的,以 study 业务开发为例:

MVC 模式

即 Model-View-Controller 模式。MVC模式是一种设计软件的模式,不是一种架构。在传统三层架构中,MVC的理念被应用在表现层:View提交请求数据给Controller,Controller返回数据用于渲染View,两者之间以Model(VO - ViewModel)的形式进行通信

传统三层架构

传统三层架构是一种典型的、基于贫血模型的、面向过程的JavaWeb分层方式

(1)数据访问层(DAL - Data Access Layer)即对包括数据库在内的数据源进行操作的部分
(2)业务逻辑层(BLL - Business Logic Layer)即对业务数据进行逻辑处理的部分
(3)表现层(UI - User Interface)即与用户交互的部分

相比更传统的架构,三层架构有着明显的优势,但也有不可忽视的缺点:分层开发后毫无疑问造成了代码量的大幅度上升和效率的下降

分层的目的是为了开发解耦和职能分工。开发人员可以只关心自己所负责的那一层,因为他只需要知道上一层提供了哪些接口,从而基于这些接口进行编程。而上一层的开发人员在不改变接口的情况下,可以任意地替换具体的实现,从而在软件团队中实现松耦合开发。

领域模型架构

领域模型的概念源于2004年出版的经典著作《Domain-Driven Design –Tackling Complexity in the Heart of Software》(《领域驱动设计:软件核心复杂性应对之道》,简称DDD):

每个软件程序的目的都是为了执行某项活动,或是满足用户的某种需求。用户会把软件程序应用于某个主题区域,这个区域就是软件的领域。一些领域涉及物质世界,例如机票预订程序的领域中包括飞机乘客在内。有些领域则是无形的,例如会计程序的金融领域。
——摘自《领域驱动设计》第一章

该著作提出,设计软件分为两个步骤:

(1)由领域专家、开发人员、设计人员就某一领域进行交流,从中发现领域概念,并根据需要,划定边界,将边界内的概念抽象为领域模型。
(2)由领域模型驱动软件设计,用代码来实现该模型。

基于此,DDDSample官网提出了另一种三层架构(参考链接)

该架构分为界面(Interfaces)、应用(Application)和领域(Domain)三层,以及一个基础设施(Infrastructure),是一种基于充血模型的、面向对象的分层方式。其各层职责如下

1、界面层(Interface)

负责所有与外部系统的交互,包括WebService、RMI或REST等。包括外观(Facade)、装配(Assembler)和数据传输对象(DTO)三类组件:

DTO组件:因为领域对象不适合暴露给用户,因此需要在返回给用户之前,重新封装为DTO,只暴露我们希望暴露的内容。同时,DTO还有减少请求的次数、简化传输对象、避免代码重复等作用。

Assembler组件:正如其字面上的含义,Assembler是一个装配工人,负责DTO与领域对象的转换。

Facade组件:Facade是外观模式的践行者,作用与传统三层架构的Controller类似,负责将一个或多个service方法组合起来,然后封装为一个接口提供为外部系统。换句话说,他负责将外部请求委派给一个或多个service进行处理。他本身不处理任何业务逻辑。

2、应用层(Application)

应用层的主要组件就是Service,其粒度与传统三层架构的service一致。差别在于,传统三层架构的service层负责业务逻辑的处理,而领域模式三层架构的service只负责将业务委派给领域对象进行处理。

3、领域层(Domain)

这一层是整个软件的核心,几乎包括了所有的业务逻辑。他包括了Entity(实体)、Value Object(值对象)、Domain Event(领域事件)和Repository(仓储)等领域组件。下面以时下很火的共享自行车为例来简单解释这几个组件:

Entity/Value Object:实体是一个在业务领域有着唯一标识的对象。实体有属性和状态,有业务行为,其业务行为会影响他的属性和状态。而值对象呢,用于描述没有唯一标识的对象。

举个栗子,当我们希望对每一辆共享自行车进行管理时,他应该被设计为实体,有唯一编号作为标识,有颜色、重量、价格、品牌等属性,有位置状态、使用状态,有租赁行为。当其被租赁时,其位置状态和使用状态可能发生改变。

同样是共享自行车,如果我们的系统只为了统计各地区各品牌自行车的使用情况,即我们只关心他是什么,而不关心他是谁,那么他应该被设计为值对象。

Domain Event:简单的说,实体触发事件,实体绑定事件。用户的租赁行为会触发租赁事件;而自行车绑定了租赁事件,当事件发生时,自行车的使用状态发生改变。

Repository:仓储类似于传统三层架构的DAO接口,但只是接口,不包括实现。

4、基础设施(Infrastructure)

作为基础设施,Infrastructure负责给三层架构提供支持。所有与具体平台、框架相关的实现都会在这一层实现,以免影响三层架构职责的纯粹性、以及污染领域模型。对象持久化的具体实现也放在基础设施里。

领域模型架构与传统三层架构

DDD把领域模型的重要性提高到了数据模型之上,在传统三层架构下。我们将项目结构分为 Controller,Service,DAO 这三个主要的层,所有的业务逻辑都在 Service 中体现,而我们的实体类 Entity 却只是充当一个与数据库做 ORM 映射的数据容器而已,它并没有反映出模型的业务价值。所以又把这种模型称为“贫血模型”。“贫血模型”有什么坏处呢?在我们的代码中将会到处看到各种的 setter 方法和各种各样的参数校验的代码,尤其是在 Service 层,但是这些代码它并没有反映出它的业务价值。这就是事务脚本的架构下,所呈现出来的弊端,这种模式下认为数据模型优先,所以会导致开发人员和产品经理在讨论问题的时候,完全是从两个角度在思考问题。开发人员听到需求后,脑袋里想的并不是如何反应出业务的价值,而是考虑的是数据库表怎么设计,字段该怎么加这些问题。所以 DDD 中提出了通用语言这么一个概念,并且基本将通用语言的概念贯穿于整个落地的过程。这样会大大的减少成员之间的沟通成本。

选型

当业务逻辑的复杂程度在可控能力范围之内时,建议采用面向过程的传统三层架构;反之,建议使用领域模型架构。

目前碰到的大部分项目均可采用传统三层架构


转载请注明来源。 欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。 可以在下面评论区评论,也可以邮件至 sharlot2050@foxmail.com。

文章标题:传统三层架构和领域模型三层架构

字数:2k

本文作者:夏来风

发布时间:2021-04-26, 00:28:36

原始链接:http://www.demo1024.com/blog/java-ddd-mvc/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。