首页
Preview

在Spring应用中的清洁架构

在本文中,我们将探讨Spring应用程序的基本架构原理。我们将通过“干净的架构”(软件架构和设计的工匠指南)的视角来观察它。我们将在开发基于REST的Spring应用程序的过程中,丰富、应用和映射各种原则到最佳的库。最终,我们将清楚地了解“干净架构”在Spring应用程序中所扮演的角色。

一个好的软件产品的价值不仅在于它的行为,还在于它的结构。我们将关注后者。

架构

我从这本书中得到的最重要的教训是:除非必须,否则要推迟做出决策。始终保持你的选择。不要在必须的情况下甚至强加扩展机制/模式。问问自己:

  • 我是否真的需要数据库,或者我的系统使用文件同样可以工作吗?
  • 我真的需要一个图形界面吗?
  • 我的图形界面真的需要用特定的框架编写吗?
  • 我真的有这么多的流量需要微服务架构吗?

你绑定在技术上的时间越短,你的程序就会越好。我们越不需要运行迁移,我们就越不需要处理那些我们一开始就不需要的升级功能,我们最终维护起来就越好。

大部分长期存在的软件成本来自于维护,而不是开发。

因此,让我们通过上述问题分析一下内部边界、策略和典型Spring应用程序提供给我们的偏见决策。

边界和策略

让我们考虑支持博客的后端系统的构建块。

首先,如果你考虑“级别”的应用程序,以数据在持久化之前通过多少层传递的层次来衡量,我们有:

  • 实体位于中心
  • 围绕它们构建的用例(验证、拼写分析、批准系统)
  • 接口适配器(Spring控制器)
  • 外部接口(Web)

后端系统的边界

从上面的边界中,我们必须保留有关依赖规则的几个事实。

内部圆圈不知道外部圆圈的存在。当数据穿过圆圈时,它总是以最方便内部圆圈的形式存在。外圈声明的数据格式不能在内圈中使用。

Spring应用程序架构

接下来,让我们看看Spring Web应用程序向我们提供的结构和库。

即使存在将行数据直接转换为符合REST规范的格式的新库(Spring Data Rest),它们可能会使你难以向应用程序添加用例。

Spring Stack允许你在实体圆圈的外层定义你的用例(或策略),通过使用映射器、设计模式和转移对象的定义。

首先,转移对象是数据的表示形式,正如它通过接口适配器从Web传输。在Sprint Rest World中,接口适配器是RestController。转移对象还代表了仅存在于服务间通信上下文中的短暂数据转换。

Spring应用程序中用例的内部结构

正如你可以从上面所示的图表中看到的那样:

  • 数据从控制器传递到服务。
  • 这个服务扮演一个外观的角色,或者调用一个外观,而这个外观调用一系列其他服务来执行必要的操作和转换,最终将数据持久化。
  • 映射器负责将转移对象转换为实体。考虑到后者是内部圆圈的一部分,因此不应将转移对象用作实体。显然,内圈不应该知道外圈的任何信息。

插件架构

现在,我们已经知道了Spring应用程序的内部结构,是否还有其他库可以利用呢?

从外到内:

  • 首先,我们想构建一个API,能够暴露API规范将是不错的。因此,我们将插入springfox swagger2库
  • 其次,很明显,在数据进入系统之前验证数据将是很好的。为此,Spring自带了一个库,建立在hibernate validator之上。由于验证器与转移对象无缝集成,因此非常方便。
  • 此外,编写转移对象可能很繁琐,我们也会使它们从一开始就是不可变的,以免以后担心。为了方便起见,我们将使用Project Lombok
  • 此外,我们希望从转移对象到数据转换尽可能快,可能是我们可以尽可能少交互的库。请记住,从一个圆圈到另一个圆圈移动的数据结构本身必须是简单的。多年来,我发现MapStruct是最好的解决方案。这个库的权衡是你必须自己编写多少代码,以及你可以有多快。我们涉及的反射越多,代码就越慢。一个统计表
  • 最后,我们希望给我们的数据库一个结构,对其进行版本控制,将其映射到我们的实体上,对其进行控制。因此,有人可以使用LiquibaseFlyway。我个人更喜欢Flyway

源代码

源代码在Github上。请随意克隆存储库并进行实验。毫无疑问,我们将引导你了解其中最重要的部分。作为项目前提条件,你应该已经安装了dockerdocker-compose。请注意项目中的_docker-compose.yaml_文件。因此,我们已经为你准备了一个pg-admin实例,你只需运行以下命令即可开始操作:

docker-compose up -d

凭据可以在docker-compose文件中找到和更改。一旦数据库启动,执行项目的主类(与任何其他Spring项目一样)。

由于我们所描绘的插件架构,让我们考虑以下图表。我们可以观察到数据在最终持久化到我们的系统之前所经过的路径。

从Clean Architecture结构的角度来看:

  • Rest Controller是你的接口适配器,是应用程序外部信息的入口点。
  • “Use Case Layer”包含所有子层,负责数据操作、验证以及控制数据流向实体的任何内容。
  • 在这种情况下,JPA实体方便地匹配JPA实体,但在更具体和实际的情况下,我们将拥有不同类型的实体。

通过Spring的请求处理路径

Spring配置

由于Spring-boot及其自动配置,我们所要做的很少。实际上,你在应用程序中的唯一配置是Swagger使用的Docket Bean的自定义字段。

Swagger配置

传输对象

我们数据转换的单元是传输对象。毫无疑问,我们通常会使用注释来轻松地启用Hibernate Validator功能并添加Swagger文档。同样,我们使用lombok来减少样板代码。最终,它们都被简化为我们使用的框架的一堆配置类。

带有Hibernate验证的传输对象

控制器

我们也使用注释来装饰接口适配器。它们的主要目的应该是数据验证。因此,用例、业务逻辑将在服务层中编写。

控制器,API层

服务

我们的服务将从数据库保存、更新、获取或删除数据。因此,它们操作Map Struct映射器,并在数据不当时强制实施应用程序级别的某种行为。

Post服务

映射器

映射器也是应用程序的重要组成部分。它们将代表我们自己写的太多样板,因此我们使用MapStruct。看看这个库为我们保护的样板代码的数量。

从传输对象到实体的映射器

存储库

或数据访问层是应用程序的边界,负责与数据库交互。

DAO层

实体

最后,为了确保数据的完整性,我们的JPA实体还添加了一些元数据。毫无疑问,我们使用数据库进行此类操作的次数越少,就越好。

通过Hibernate映射到数据库的实体

数据库初始化脚本

值得一提的是,我们通过使用Flyway在启动时对数据库结构进行建模。

初始化数据库脚本

最后的话

我们希望你了解了Spring应用程序内部结构的更多内容,以及如何构建应用程序,以便以后不必维护或扩展它而头痛。

将大部分编写的代码作为其他库的配置来处理既是一种良好的实践,也是有效的。然而,有时候选择哪个库更好有些烦人。我们推荐使用lombok、hibernate validator和mapstruct。如果你想公开API规范,请导航到http://localhost:8080/v2/api-docshttp://localhost:8080/swagger-ui/查看它们。

最后,无论你使用的是哪种技术,都请查看《Clean Architecture: A Craftsman’s Guide to Software Structure and Design (Robert C. Martin Series)》

译自:https://medium.com/geekculture/clean-architecture-in-a-spring-application-312e119ee8ec

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
阿波
The minute I see you, I want your clothes gone!

评论(0)

添加评论