【发布时间】:2012-10-04 17:26:19
【问题描述】:
使用以数据为中心的方法在 Spring MVC 中维护清洁架构
我正在尝试为我们正在开发的基于 Java 的新 Web 应用程序(门户类型应用程序)的前端规划架构。我想从第一天开始就做到这一点,我想在这里开始讨论,以帮助我在我的架构设计中实现 Bob 大叔的 Clean Architecture。
下面是我们的技术栈的简要介绍,从上到下(技术不重要,结构才是):
- Oracle 数据库
- Oracle Service Bus 使用 WSDL 公开服务
- JAX-WS 从 WSDL 生成 Java 类(我们称之为“生成的服务层”)
- 由映射到生成的数据对象的 POJO 组成的域模块
- 将“生成的服务层”暴露给前端应用程序的消费者模块
- 基于 Spring MVC 的前端模块,使用 FreeMarker 呈现视图
一个关键点:
特别是,在外圈中声明的事物的名称不能被内圈中的代码提及。这包括函数、类。变量,或任何其他命名的软件实体。
为了遵循 Bob 的简洁架构,我在应用程序逻辑(即他的架构中的“用例”层)的放置位置上与自己反复讨论。
这是我想出的方法:
第 1 层 - 实体
实体封装了企业范围的业务规则。
这是包含 domain-objects 的 Domain 模块所在的地方,这些是自包含对象,彼此之间的依赖关系最小。只有与对象本身相关的逻辑可能存在于这些域对象上,而没有特定于用例的逻辑。
对我们数据库的访问是通过使用转换数据的服务总线的 WSDL 公开的,而不是像 JPA 或 Hibernate 这样的 ORM。正因为如此,我们没有传统意义上的“实体”(带有 ID),而是一种以数据为中心的方法,使该层成为数据访问层,由消费者模块。
第 2 层 - 用例
这一层中的软件包含特定于应用程序的业务规则。
这是特定于我们应用程序用例的逻辑所在。对此层的更改不应影响数据访问层(第 1 层)。对 GUI 或框架实现 (Spring MVC) 的更改不应影响这一层。
这有点棘手:
由于我们的数据访问层(在第 1 层)必须保持干净的应用程序逻辑,我们需要一个层来促进以适合用例的方式使用该层。我发现这个问题的一个解决方案是使用我选择称为 MVC-VM 的“MVVM-模式”的变体。请参阅下面的说明。其中的“VM”部分位于此用例层中,由封装此用例特定逻辑的*ViewModel-类表示。
第 3 层 - 接口适配器
这一层中的软件是一组适配器,可将数据从最适合用例和实体的格式转换为最适合某些外部机构(如数据库或 Web)的格式。
这就是我们 GUI 的 MVC 架构所在的地方(我们的“MVC-VM”中的“MVC”)。本质上,这是Controller-classes 从*ViewModel-classes 获取数据并将其放入 Spring MVC 的 ModelMap 对象中,这些对象直接由 View 中的 FreeMarker-templates 使用。
在我看来,在我们的例子中,服务总线也属于这一层。
第 4 层 - 框架和驱动程序
通常,您不会在这一层编写太多代码,除了与向内的下一个循环通信的胶水代码。
这一层实际上只是我们应用程序中的一个配置层,即 Spring 配置。例如,这就是我们指定 FreeMarker 用于呈现视图的地方。
模型视图 ViewModel 模式
MVVM 有助于将图形用户界面(作为标记语言或 GUI 代码)的开发与称为模型(也称为数据模型)的业务逻辑或后端逻辑的开发明确分离从视图模型)。 MVVM 的视图模型是一个值转换器,这意味着视图模型负责从模型中公开数据对象,以便这些对象易于管理和使用。
有关 MVVM 模式的更多信息,请访问Wikipedia。
MVC-VM 角色将在我们的应用程序中实现,如下所示:
-
Model - 由视图模板使用的 Spring MVC 中的
ModelMap数据结构简单地表示。 - 查看 - FreeMarker 模板
Controller Spring 的
Controller-classes 将 HTTP URL 请求 定向到特定 handlers(以及 FrontController 等功能) )。这些类中的处理程序负责从用例层获取数据并在显示数据时将其推送到视图模板(HTTPGET),以及向下发送数据以进行存储(HTTPPOST)。这样,它本质上是作为 ViewModel 和 View 之间的 binder 的,使用 Model。ViewModel - 这些类负责 1) 以 View 可用的方式构造来自数据访问层的数据,以及 2) 处理来自 View 的数据输入。 “处理”意味着验证和分解数据,以便可以将其发送到堆栈以进行存储。该层将在我们的 Spring MVC 前端模块中的
viewmodel包中采用<UseCase>VM-classes 的形式。
这里的一个关键组件是在 Spring MVC 中 ModelMap 和 FreeMarker 模板之间发生的隐式绑定。模板仅使用模型 (ModelMaps),其中控制器已将数据置于其可以使用的格式中。这样我们就可以制作这样的模板:
<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>Models 并将它们放在 MVC 项目中,但根据 Bob
模型可能只是从控制器传递到用例,然后从用例返回到演示者和视图的数据结构。
这意味着我的模型对象应该是“哑”的(就像 Spring 中的简单 Map.ModelMap),然后控制器负责将用例类中的数据放入此映射结构.
同样,我的用例类采用什么形式? <UseCase>Interactor呢?
但总而言之我意识到 MVC-MV 的东西是过度设计的(或者根本不正确) - 正如“mikalai”在下面指出的那样,它基本上只是当前形式的两层应用程序;一个数据访问层和一个前端 MVC 层。就这么简单。
【问题讨论】:
-
Adam 是对的——好的架构是多年来一直在建造的,而不是从头开始的。因此,您的数据驱动应用程序实际上是 2(!)层系统。您可以轻松地从 1) UI 层开始成为任何合适的 MVC 框架(因为它在 UI 增长的同时处理复杂性),以及 2) 通过一些通用接口 (Repository
) 的简单数据访问层。然后,在添加模式和更多功能后,您应该重构。您应该添加抽象来解决您的确切问题,而不是一开始就计划它们。
标签: java spring-mvc model-view-controller mvvm architecture