软件设计最佳实践,探讨按层、按特性和六边形架构/端口和适配器的代码结构。
在这篇文章中,我将探讨如何组织我们的代码并讨论最佳实践,涵盖三种不同的方法:按层、按特性和六边形架构/端口和适配器,以及它们的优缺点。
在探讨不同的代码结构方法之前,我们需要了解基本的软件设计原则:
•内聚性(Cohesion) :指模块内部的类彼此相关的程度。•耦合性(Coupling) :指不同模块之间的依赖程度。
1*2Q8ye2K8hYRtNOMHWxoOBQ.png
•模块性(Modularity) :指软件系统被分成独立模块的程度。每个模块封装了一组特定的功能,并被设计成能够独立工作,通过明确定义的接口与其他模块交互。•抽象化(Abstraction) :隐藏实现细节,只通过接口暴露必要的功能。•关注点分离(Separation of Concerns) :具有明确定义的各个部分,每个部分处理特定的关注点。•封装性(Encapsulation) :将数据和方法捆绑到单个模块或类中,以隐藏内部细节。
让我们更仔细地看看内聚性和耦合性?
内聚性描述软件的 关注点有多集中。它与单一职责原则密切相关。
•高内聚性 意味着模块内的类彼此密切相关,并共享一个共同的、明确定义的目的。•低内聚性 意味着模块内的类关系不紧密,缺乏明确的目的,具有无关的责任。
遵循的最佳实践是追求高内聚性和松耦合。
1*6DpCfIP-T09sZBfxu8G2DA.png
松散的耦合被认为是计算机系统良好结构和良好设计的迹象,与高内聚性结合使用时,可实现高可读性和可维护性。
1*WVS54T-5rqEvL7xf1AeADA.png
现在,让我们探讨不同的代码组织方式。首先,我将介绍按层组织,然后是按特性组织,对比这两者。之后,我们将探讨六边形架构/端口和适配器模式。
1*fmBZBkeP4RAmM9DwFoynyQ.png
按层组织
它表示一个项目结构,其中类被组织到多个层中,每个层负责一组特定的功能。
other
src
├── main
│ ├── java
│ │ └── com
│ │ └── app
│ │ ├── service
│ │ │ └── UserService.java
│ │ │ └── OrderService.java
│ │ │ └── ProductService.java
│ │ ├── domain
│ │ │ └── User.java
│ │ │ └── Order.java
│ │ │ └── Product.java
│ │ ├── repository
│ │ │ └── UserRepository.java
│ │ │ └── OrderRepository.java
│ │ │ └── ProductRepository.java
│ │ ├── controller
│ │ │ └── UserController.java
│ │ │ └── OrderController.java
│ │ │ └── ProductController.java
典型的层包括:
1.表示层(Presentation Layer) :负责处理用户交互并向用户呈现信息。通常包括与用户界面、控制器和视图相关的组件。2.服务层(Service Layer) :包含业务逻辑并提供演示层所需的数据。3.领域包(Domain Package) :该包包含领域实体。4.数据访问层(Data Access Layer) :该层处理数据到/从数据库的持久化和检索。5.基础设施包(Infrastructure Package) :该包提供支持应用程序操作的服务。它可能包括用于日志记录、配置、安全等横切关注点的组件。
使用 按层组织 的一些缺点:
•低内聚性:不相关的类被组合到同一个包中。•高耦合性•封装性差:大多数类是公共的,因此我们无法将类设置为包私有,因为它们在其他层中是需要的。•低模块性:由于每个包包含与特定层相关的类,因此很难将代码分解为后来的微服务。•可维护性差:由于类散布在不同的包中,很难找到正在寻找的类。•它促进了数据库驱动设计,而不是领域驱动设计。
按特性组织
它表示一种根据功能或特性而不是层次结构组织代码的结构。在这种方法中,每个包代表一个独特且独立的功能。
目标是将与特定特性相关的所有组件(如控制器、服务、存储库和领域类)组合到一个包中。
other
src
├── main
│ ├── java
│ │ └── com
│ │ └── app
│ │ ├── user
│ │ │ ├── UserController.java
│ │ │ ├── UserService.java
│ │ │ └── UserRepository.java
│ │ ├── order
│ │ │ ├── OrderController.java
│ │ │ ├── OrderService.java
│ │ │ └── OrderRepository.java
│ │ ├── product
│ │ │ ├── ProductController.java
│ │ │ ├── ProductService.java
│ │ │ └── ProductRepository.java
使用此结构的一些好处包括:
•高内聚性•低耦合性•强封装性:允许某些类将其访问修饰符设置为包私有而不是公共。•高模块性:由于每个包包含与特定功能相关的类,因此很容易将代码分解为后来的微服务。•可维护性:减少了在包之间导航的需求,因为所有与特性相关的类都在同一个包中。•促进了领域驱动设计
六边形架构/端口和适配器模式
六边形架构,也称为端口和适配器,是由阿利斯泰尔·科克本博士在他2005年的一篇文章中介绍的软件架构模式。[1]
该模式通过保持核心业务逻辑独立于外部细节,并不紧密耦合于数据库、用户界面或外部服务等外部依赖,促进了关注点的隔离/分离。
这使得测试、维护和发展系统更加容易。
1*wF_VJcN5dpwCQB_sx1z2_w.png
在此模式中:
1.领域/核心(Domain / Core) 代表应用程序的业务逻辑或领域(应用程序的核心)。2.端口(Ports) 端口是核心定义的接口,允许与外部组件进行交互。这些可以包括服务、存储库或任何外部依赖的接口。3.适配器(Adapters) 适配器是端口的实现。它们将核心应用程序连接到数据库、用户界面和外部服务等外部组件。适配器可以针对不同的技术或协议进行特定的实现。4.主要操作者(Primary actors) 系统的使用者,如webhook、UI请求或测试脚本。5.次要操作者(Secondary actors) 被应用程序使用的这些服务是 Repository(例如数据库)或 Recipient(例如消息队列)。
1*AUdml5HCo92xf8WgHkZfRA.gif
六边形形状:
六边形形状象征着核心应用程序位于中心,周围是适配器。这个形状代表了核心与其外部依赖之间的明确分离。
顶级包结构应如下所示:
other
src/main/
java
mina
dev
<servicename>
adapters
config
core
<ServiceApplication>.java
根包应只包含包:core
、adapters
和config
。
•core
包含服务的所有领域逻辑。它可以包含子包。•端口应该位于 core
包中:端口只是由核心声明的要调用或由适配器实现的接口。•adapters
包含所有适配器实现代码。它可以包含子包以按单个适配器或技术组织适配器代码。•config
包含用于连接不同组件的配置类。
包依赖规则:
•根包可以依赖于所有其他包。•config
包可以依赖于 core
和 adapters
。•adapters
可以依赖于 core
,但不可以依赖于 config
。•core
不得依赖于其他任何包。
希望这篇文章能够帮助您更好地理解不同的代码结构。
引用链接
[1]
软件架构模式。: alistair.cockburn.us/hexagonal-a...