Docker MariaDB 整洁架构
一个使用 Fiber 实现的稍微复杂的 REST 应用,展示了整洁架构,其中 MariaDB 作为依赖项,并使用 Docker 进行容器化。
先决条件
- Docker Compose 用于运行应用。
- 支持
sh
、make
和curl
的 Shell,用于端到端测试。UNIX 系统或 WSL 应该可以正常工作。 - Postman,如果您想使用 GUI 测试此 API。
应用
此应用是一个稍微复杂的 REST API 示例,它有四个主要端点。公共用户可以访问 User
、Auth
和 Misc
主要端点,但他们无法访问 City
端点(因为它受到保护)。如果有人想访问该端点,他们必须先通过 Auth
端点登录,之后才能访问 City
端点。
此应用使用 MariaDB 作为数据库(容器化),并使用 JWT 作为认证机制。此应用还展示了如何在整洁架构中执行一对多关系映射(一个用户可以拥有多个城市),以及如何在 Go 中实现 JOIN
SQL 子句。
整洁架构
整洁架构是由 Robert C. Martin(也称为 Uncle Bob)引入的一个概念。简单来说,此架构的目的是实现职责的完全分离。以此方式构建的系统可以独立于框架、可测试(易于编写单元测试)、独立于 UI、独立于数据库以及独立于任何外部机构。当您使用此架构时,更改 UI、数据库或业务逻辑都变得简单。
使用此架构时应牢记的一点是关于依赖规则。在整洁架构中,源代码依赖只能指向内部。这意味着系统的“内圈”完全不能了解外部世界。例如,在上图中,用例知道实体,但实体不能知道用例。外圈中使用的数据格式不应被内圈使用。
正因为如此,当您更改位于最内圈的内容(例如实体)时,通常您必须更改外圈。然而,如果您更改的不是最内圈的内容(例如控制器),您不需要更改用例和实体(您可能必须更改框架和驱动程序,因为它们相互依赖)。
如果您想了解更多关于整洁架构的信息,请参考我下方附上的文章。
系统架构
为了清晰起见,这是展示此 API 系统架构的图表。
请参考下表,了解此应用中使用的各层及其术语/文件名。项目结构参考自此项目。在 internal
包中,根据功能职责对包进行了分组。如果您打开包,您将看到代表整洁架构层的文件。
依赖图是直观的:handler/middleware 依赖于 service,service 依赖于 repository,repository 依赖于 domain 和数据库(通过依赖注入)。所有这些层都通过上面图片中提到的基础设施(Fiber、MariaDB 和认证服务)实现。
我稍微修改了此应用中的层,以符合我自己的整洁架构偏好。
架构层 | 对应层 | 文件名 |
---|---|---|
外部接口 | 展现器和驱动程序 | middleware.go 和 handler.go |
控制器 | 业务逻辑 | service.go |
用例 | 仓库 (Repositories) | repository.go |
实体 (Entities) | 实体 (Entities) | domain.go |
基本上,一个请求首先会通过 handler.go
(和 middleware.go
)。之后,程序会调用 service.go
所请求的 repository 或 use-case。该控制器 (service.go
) 会调用符合 domain.go
的 repository.go
,以便完成 service.go
所请求的任务。请求的结果将由 handler.go
返回给用户。
简而言之
handler.go
和middleware.go
用于接收和发送请求。service.go
是业务逻辑或控制器(有些人可能有不同的看法,但这只是我的主观意见)。repository.go
用于与数据库交互(用例)。domain.go
是程序使用的数据模型的“形状”。
为了完整起见,以下是项目结构的功能职责。
internal/auth
用于管理认证。internal/city
用于管理城市。此端点受到保护。internal/infrastructure
用于管理应用的基础设施,例如 MariaDB 和 Fiber。internal/misc
用于管理杂项端点。internal/user
用于管理用户。此端点不受保护。
请参考代码本身获取更多详细信息。我在代码中添加了所有注释,希望足够清晰!
API 端点 / 功能
此 API 分为四个“主要端点”,分别是杂项、用户、认证和城市。
杂项
此处分类的端点是杂项端点。
GET /api/v1
用于健康检查。
用户
此处分类的端点是用于对“用户”领域执行操作的端点。
GET /api/v1/users
获取所有用户。POST /api/v1/users
创建用户。GET /api/v1/users/<userID>
获取特定用户。PUT /api/v1/users/<userID>
更新特定用户。DELETE /api/v1/users/<userID>
删除特定用户。
认证
此处分类的端点是用于执行认证的端点。在我看来,这属于框架层/实现细节,因此此端点没有“领域”,您可以将此端点作为其他端点的增强。此 API 中的认证使用 JSON Web Tokens 完成。
POST /api/v1/auth/login
作为数据库中 ID 为 1 的用户登录。将返回 JWT,该 JWT 将存储在 cookie 中。POST /api/v1/auth/logout
退出登录。此路由将从 cookie 中移除 JWT。GET /api/v1/auth/private
访问受保护的路由,显示当前(有效)JWT 的信息。
城市
此处分类的端点是用于对 City
领域执行操作的端点。此处端点通过 cookie 中的 JWT 受到保护,因此如果您要使用此端点,请确保您先登录(或至少拥有有效的 JWT)。
GET /api/v1/cities
获取所有城市。POST /api/v1/cities
创建新城市。GET /api/v1/cities/<cityID>
获取特定城市。PUT /api/v1/cities/<cityID>
更新特定城市。DELETE /api/v1/cities/<cityID>
删除特定城市。
安装
为了运行此应用,您只需执行以下命令。
- 克隆仓库。
git clone git@github.com:gofiber/recipes.git
- 切换到此仓库。
cd recipes/docker-mariadb-clean-arch
- 立即使用 Docker 运行。运行此命令后,迁移脚本将自动运行,以填充您的 Docker 化 MariaDB。
make start
- 使用 Postman 测试(将请求 URL 设置为
localhost:8080
)或使用创建的端到端测试脚本进行测试。请记住,端到端脚本仅适用于首次运行。如果您尝试第二次运行,您可能无法获得所有完美的结果(因为 MariaDB 中的自动递增)。如果您想再次运行测试套件,请先运行make stop
和make start
。
make test
- 拆除或停止容器。这将同时删除创建的 Docker 数据卷和镜像。
make stop
完成!
常见问题
我在网上找到的一些常见问题。请注意,答案大多是主观的。
问:这是实现整洁架构的正确方式吗?
答:不是。有很多方法可以实现整洁架构 - 这个示例是其中之一。有些项目可能比这个示例更好。
问:为什么认证是实现细节?
答:认证是实现细节,因为它不与用例或 repository/interface 层交互。认证有点特殊,可以作为中间件在其他任何路由中实现。请注意,这是我的主观意见。
问:这是推荐的 Fiber 项目结构方式吗?
答:不是。和其他 Gopher 一样,我建议您从一个单独的 main.go
文件开始您的项目。有些项目不需要复杂的架构。当您开始看到分支的需求时,我建议您根据功能职责拆分您的代码。如果您需要更严格的结构,那么您可以尝试采用整洁架构或您认为合适的任何其他架构,例如 Onion、Hexagonal 等等。
问:这只适用于 Fiber 吗?
答:不是。您可以简单地调整 handler.go
和 middleware.go
文件,以将外部接口/presenters 和 drivers 层更改为其他内容。您可以使用 net/http
、gin-gonic
、echo
等等。如果您想更改或添加数据库,您只需相应地调整 repository.go
文件。如果您想更改业务逻辑,只需更改 service.go
文件。只要职责分离做得好,您就不需要更改太多东西。
问:这可以用于生产环境吗?
答:我尽量让它尽可能适用于生产环境😉
改进
本项目可以实现的进一步改进:
- 增加更多测试和 Mock,尤其是单元测试(整洁架构最适合执行单元测试)。
- 增加更多 API 端点。
- 在 repository 层添加缓存机制,例如 Redis。
- 添加事务支持。
- 也许尝试将 S3 后端集成到 repository 层(MinIO 是个不错的选择)。
- 也许在
internal
包中添加一个domain
文件夹,我们将实体放在那里?
讨论
请随时在此仓库中创建 Issue(或者在 Fiber 的 Discord 服务器上提问),以便我们一起讨论!
参考资料
感谢我阅读并从中获得灵感的文章及其作者!
- Angad Sharma 的 Clean Architecture
- Uncle Bob 的 Clean Architecture
- Elton Minetto 的 Clean Architecture with Go
- Elton Minetto 的 Clean Architecture with Go Part 2
- @namkount 的 Creating Clean Architecture using Go
- Kenta Takeuchi 的 Dive to Clean Architecture with Go
- Reshef Sharvit 的 Go and Clean Architecture
- Jin Feng 的 Go Microservices with Clean Architecture
- Go Project Layout Repository
- Imam Tumorang 的 Trying Clean Architecture on Go