使用以数据为中心的方法在 Spring MVC 中维护整洁的架构
我正在尝试为我们正在制作的基于 Java 的新 Web 应用程序(门户类型应用程序)的前端绘制架构图。我想从第一天开始就做到这一点,我想在这里开始讨论,以帮助我在我的架构设计中实施鲍勃叔叔的清洁架构。
这是我们的技术堆栈的快速概述,从上到下(技术不重要,结构很重要):
- 甲骨文数据库
- Oracle Service Bus 使用 WSDL 公开服务
- JAX-WS 从 WSDL 生成 Java 类(我们称之为“生成的服务层”)
- 由映射到生成的数据对象的 POJO 组成的域模块
- 将“生成的服务层”暴露给前端应用程序的消费者模块
- 基于 Spring MVC 的前端模块,使用 FreeMarker 呈现视图
一个关键点:
特别是,在外圈中声明的事物的名称不能被内圈中的代码提及。这包括函数、类。变量或任何其他命名的软件实体。
试图坚持 Bob 的清洁架构,我与自己反复讨论了应用程序逻辑的放置位置,即他的架构中的“用例”层。
这是我想出的方法:
第 1 层 - 实体
实体封装了企业范围的业务规则。
这是包含域对象的域模块所在的位置,这些是自包含对象,彼此之间的依赖性最小。只有与对象本身相关的逻辑可能存在于这些域对象上,而没有特定于用例的逻辑。
与 JPA 或 Hibernate 等 ORM 不同,使用转换数据的服务总线通过 WSDL 公开对我们数据库的访问。正因为如此,我们没有传统意义上的“实体”(带有 ID),而是一种以数据为中心的方法,使该层成为数据访问层,由消费者模块呈现给应用程序的其余部分。
第 2 层 - 用例
该层中的软件包含特定于应用程序的业务规则。
这就是特定于我们应用程序用例的逻辑所在。对此层的更改不应影响数据访问层(第 1 层)。对 GUI 或框架实现 (Spring MVC) 的更改不应影响这一层。
这是有点棘手的地方:
由于我们的数据访问层(在第 1 层)必须保持干净的应用程序逻辑,我们需要一个层来促进以适合用例的方式使用该层。我发现这个问题的一个解决方案是使用我选择称为MVC-VM的“ MVVM 模式”的变体。请参阅下面的说明。其中的“VM”部分位于此用例层中,由封装此用例特定逻辑的类表示。*ViewModel
第 3 层 - 接口适配器
该层中的软件是一组适配器,可将数据从对用例和实体最方便的格式转换为对某些外部机构(如数据库或 Web)最方便的格式。
这就是我们 GUI 的 MVC 架构所在的地方(我们的“MVC-VM”中的“MVC”)。本质上,这是当Controller
-classes 从 -classes 获取数据*ViewModel
并将其放入 Spring MVC 的ModelMap
ojects 中,这些 ojects 由 View 中的 FreeMarker-templates 直接使用。
在我看来,在我们的例子中,服务总线也属于这一层。
第 4 层 - 框架和驱动程序
通常,您不会在这一层编写太多代码,除了与内部下一个循环通信的胶水代码。
这一层实际上只是我们应用程序中的一个配置层,即 Spring 配置。例如,这将是我们指定 FreeMarker 用于呈现视图的地方。
模型视图 ViewModel 模式
MVVM 有助于将图形用户界面(作为标记语言或 GUI 代码)的开发与称为模型(也称为数据模型)的业务逻辑或后端逻辑的开发明确分离,以将其与视图区分开来模型)。MVVM 的视图模型是一个值转换器,这意味着视图模型负责从模型中公开数据对象,以便这些对象易于管理和使用。
有关 MVVM 模式的更多信息,请访问Wikipedia。
MVC-VM 角色将在我们的应用程序中实现,如下所示:
- 模型-
ModelMap
由视图模板使用的 Spring MVC 中的数据结构简单地表示。 - 查看 - FreeMarker 模板
控制器Spring 的
Controller
类,将HTTP URL 请求定向到特定的处理程序(以及诸如 FrontController 之类的函数)。这些类中的处理程序负责从用例层获取数据并在显示数据时将其推送到视图模板(HTTPGET
),以及向下发送数据以进行存储(HTTPPOST
)。通过这种方式,它本质上用作ViewModel 和 View 之间的绑定器,使用 Model 。ViewModel - 这些类负责 1) 以 View 可用的方式构造来自数据访问层的数据,以及 2) 处理来自 View 的数据输入。“处理”意味着验证和分解数据,以便可以将其发送到堆栈以进行存储。该层将在我们的 Spring MVC 前端模块
<UseCase>VM
中以包中的类的形式出现。viewmodel
ModelMap
这里的一个关键组件是在 Spring MVC和 FreeMarker 模板之间发生的隐式绑定。模板仅使用模型 ( ModelMap
s),其中控制器已将数据置于其可以使用的格式中。这样我们就可以制作这样的模板:
<body>
<h1>Welcome ${user}!</h1>
<p>Our latest product:
<a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
我为冗长的解释道歉,但我无法用更少的词来解释这个(相对简单的)架构。
我将非常感谢我在这里的方法的一些输入 - 我在正确的轨道上吗?MVC-VM 的东西有意义吗?我是否违反了任何清洁架构原则?
当然有很多解决方案,但我试图找到一个解决方案,它 1) 没有过度设计和 2) 遵守 Bob 的清洁架构的原则。
更新:
我认为让我失望的关键问题是“用例”层在这个应用程序中采用什么形式。请记住,我们有一个从数据访问层获取数据的 MVC 前端。如果 MVC 部分适合 Bob 的“接口适配器”,而数据层的域模型适合 Bob 的“实体”层,那么我将实现应用程序逻辑的用例类称为什么?我很想将它们称为<UseCase>Model
s 并将它们放入 MVC 项目中,但根据 Bob
模型可能只是从控制器传递到用例,然后从用例返回到演示者和视图的数据结构。
所以这意味着我的模型对象应该是“哑巴的”(就像 Spring 中的简单Map
.一样ModelMap
),然后控制器负责将 Use Case 类中的数据放入这个 Map 结构中。
再说一遍,我的用例类采用什么形式?怎么样<UseCase>Interactor
?
但总而言之,我意识到 MVC-MV 的东西是过度设计的(或者根本不正确)——正如“mikalai”在下面指出的那样,它本质上只是当前形式的两层应用程序;一个数据访问层和一个前端 MVC 层。就那么简单。