清晰架构(Clean Architecture)的Go微服务: 程序结构
我使用Go和gRPC创建了一个微服务,并试图找出最佳的程序结构,它可以用作我未来程序的模板。 我有Java背景,并发现自己在Java和Go之间挣扎,它们之间的编程理念完全不同。我写了一系列关于在项目工作中做出的设计决策和取舍的文章。 这是其中的第一篇, 是关于程序结构的。
程序结构的资源
Go的标准程序结构的最佳资源可能是Github上的标准Go程序结构¹,但它不适合我的项目。在阅读了Sylvain Wallez的文章²之后,我终于得到了一些关于其背后原因的信息。 Go起初是专为API和网络服务器而设计,Github上的大多数Go项目都是以库的形式编写的,因此“标准Go程序结构”可能非常适合。 商业微服务项目是一种完全不同的动物,需要不同的程序结构。但我还是采用了“标准Go程序结构”中适用的一些建议,这些建议约占30%。
一般来说,创建应用程序结构有两种不同的方法:基于业务功能或基于技术结构。大家的共识³是基于业务功能的更好,对于单体项目(monolithic project)来说也确实如此。在微服务架构中,情况发生了变化,因为每个服务都有自己的源码库,这相当于已经把应用程序按业务功能查分成了一个个的微服务。因此,在每个微服务中,基于技术结构创建项目结构实际上是可行的。
我还找到了Ben Johnson关于应用程序结构⁴和包结构⁵的一些好建议。但它没有为我的项目提供完整的解决方案,所以我决定创建自己的程序结构。程序结构取决于项目要求,以下是需求。
项目需求:
1.在数据持久层上支持不同的数据库(Sql和NoSql数据库)
2.使用不同的协议(如gRPC或REST)支持来自其他微服务的数据
3.松散耦合和高度内聚
4.支持简单一致的日志记录,并能够更改它(例如,日志记录级别和日志记录库),而无需修改程序中的日志记录语句。
5.支持业务级别的事物交易。
程序结构也受到程序设计的影响。 我采用了 Bob Martin的清晰架构(Clean Architecture)⁶ 和 Go的 简洁⁷ 设计风格.
在业务逻辑方面,有三层:“模型(model)”,即域模型; “数据服务(dataservice)”,它是数据持久性(数据库)层; “用例(usecase)”,这是业务逻辑层。
顶层程序结构:

adapter: 这是应用程序和外部数据服务之间的接口,例如另一个gRPC服务。 所有数据转换都发生在这里,这样你的业务逻辑代码不需要了解外部服务的具体实现(无论是gRPC还是REST)。
cmd: 命令。 所有不同类型的“main.go”都在这里,你可以有多个。 这是应用程序的起点。
config: 设置程序和配置数据,包括配置文件。
container: 应用程序依赖注入容器,负责创建具体类型并将它们注入每个函数。
dataservice: 持久层,负责检索和修改域模型的数据。 它只依赖(depend)于模型(model)层。 数据服务可以通过RPC或RESTFul调用从数据库或其他微服务获取数据。
model: 域模型层,具有域结构(model struct)。 所有其他层依赖于此层,而此层不依赖于任何其他层。
script: 与设置应用程序相关的脚本,例如,数据库脚本。
tool: 第三方工具。
usecase : 这是一个重要的层并且是业务逻辑的切入点。 每个业务功能都由用例实现。 它是程序顶层,因此没有其他层依赖于它(“cmd”除外),但它依赖于其他层。
用例和数据服务层功能全部由接口调用,因此可以轻松更改实现。
顶级包下的子文件包:
adapter:

当程序需要与微服务或其他外部服务进行交互时,你需要创建接口以减少依赖性。例如,本程序中的“缓存服务”是一个gRPC微服务。每个外部服务都有自己的子包和文件。例如,缓存服务具有“cacheclient”包和“cacheClient.go”文件,该文件定义了与外部“缓存”微服务交互的类型和方法。
在我们的示例中,与其他数据服务不同,“cacheClient.go”文件没有定义缓存服务的接口。实际上它有一个,但是interface是在“dataservice”包中定义的,因为“缓存服务”也是一个数据服务。更明确的方法可能是在两个包中各自创建一个接口,这将保持包结构的统一。但是这两个接口将是相同且冗余的,所以我删除了适配器中的接口。
由于我们还需要将应用程序本身发布为gRPC服务,因此需要在此处创建“userclient”子包。 “generatedclient”子包是为gRPC和Protobuf生成的代码。“userGrpc.go”用来处理域模型结构和gRPC结构之间的格式转换。
cmd:

应用程序的命令,是整个程序的起点。 你可以将应用程序作为本地应用程序运行,也可以将其作为微服务应用程序运行,在这种情况下,你同时拥有客户端(grpcClientMain.go)和服务器端(grpcServerMain.go)主文件。 所有未来的主文件(main.go)也将在此处,例如,Web应用程序服务器主文件。
config:

在此保存所有应用配置文件。 “appConfig.go”负责从配置文件中读取并数据将它们加载到应用程序配置结构中。 你可以为不同的环境提供不同的配置文件(YAML文件),例如“Dev”和“Prod”。
container:

本程序中最复杂的包,它为每个接口创建具体结构并将它们注入其他层。此包中的子包结构类似于应用逻辑分层,它还具有“用例(usecase)”,“数据服务(dataservice)”和“数据存储(datastore)”层。
在本包顶层,“container.go”定义容器接口,它的实现文件“serviceContainer.go”在“servicecontainer”子包中。它是此包的入口点,它将此包中的其他代码粘合在一起。 “usecasefactory”子包中的“registrationFactory.go”和其他工厂(factory)使用工厂方法模式(factory method pattern)创建具体结构(struct)。 日志⁸不属于任何应用层,因此我为它创建了一个单独的子包“loggerfactory”。它还有一个“logger”子包来定义日志记录接口。我在文章程序容器9中解释了这个包中的所有内容。
dataservice:

域模型的持久层。 顶层只有一个文件 - “dataService.go”,它具有数据服务的所有接口,包括其他微服务提供的数据服务(例如gRPC)。 其他包只需要依赖于顶级数据服务接口,而不需要了解特定数据库的实现细节。
对于域模型中的每种类型,都有相应的数据服务。 例如,对于模型“User”,有一个“userdata”子包,它包含用户持久性服务的所有实现,包括sqldb(MySql)和CouchDB。
model:

该层不需要接口,模型都是具体结构。
Script:

目前它只有MySql的数据库脚本。
tool:

此程序包适用于第三方工具。 如果你不想直接依赖第三方库,或者需要增强这些第三方库,请在此处进行封装。 不要与“adapter”包混淆,后者也处理第三方库,但只适用于应用程序级数据服务。 “tool”包更适用于较低级别的库。
useCase:

顶级包下只有一个文件“useCase.go”,它包含所有用例接口。 目前,有三个用例:“RegistrationUseCase”,“ListUserUseCase”和“ListCourse”。
每个用例都有一个子包。 该子包中的文件定义了实现用例接口的具体结构。 所有其他包仅依赖于顶级的用例接口,不需要了解每个用例的实现细节。
可能的问题:
这个程序结构最终会产生很多小的子包,每个子包只有一个或几个文件。 这与Go习惯用法“考虑更少,更大的包¹⁰相矛盾. 以下才是习惯用法应创建的包:

如果你为其他人编写一个外部库,那么将代码放入一个大包中是一个很好的规则,因为人们不需要多个import语句来使用你的库。 但是在你自己的应用程序中,拥有小包是可以的,特别是当你只将接口暴露给其他层时。
本程序为什要用小包呢? 首先“useCase.go”只定义接口,而其他包(容器除外)仅依赖于接口,因此“useCase.go”需要一个独立的包。 其次,用文件夹分隔每个用例使程序更清晰易读。
结论:
对于gRPC微服务项目,“标准Go程序结构”可能不太适合。 基于业务逻辑而不是技术结构创建应用程序结构对单体项目是一个很好的建议,不一定适合微服务项目。 本文使用一个真实的应用程序作为示例来展示什么是一个很好的微服务应用程序结构及其背后的原因。
源程序:
完整的源程序链接 github: https://github.com/jfeng45/servicetmpl
索引:
[1][golang-standards/project-layout]
(https://github.com/golang-standards/project-layout)
[2][Go: the Good, the Bad and the Ugly]
(https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/)
[3][Package by feature, not layer]
(http://www.javapractices.com/topic/TopicAction.do?Id=205)
[4][Structuring Applications in Go]
(https://medium.com/@benbjohnson/structuring-applications-in-go-3b04be4ff091)
[5][Standard Package Layout]
(https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1)
[6][The Clean Code Blog]
(https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
[7][Go at Google: Language Design in the Service of Software Engineering]
(https://talks.golang.org/2012/splash.article)
[8][Go Microservice with Clean Architecture: Application Logging]
(https://jfeng45.github.io/posts/go_logging_and_error_handling/)
[9][Go Microservice with Clean Architecture: Application Container]
(https://jfeng45.github.io/posts/application_container/)
[10][Practical Go: Real world advice for writing maintainable Go programs]
(https://dave.cheney.net/practical-go/presentations/qcon-china.html)
清晰架构(Clean Architecture)的Go微服务: 程序结构的更多相关文章
- (转)微服务架构 互联网保险O2O平台微服务架构设计
http://www.cnblogs.com/Leo_wl/p/5049722.html 微服务架构 互联网保险O2O平台微服务架构设计 关于架构,笔者认为并不是越复杂越好,而是相反,简单就是硬道理也 ...
- Java 18套JAVA企业级大型项目实战分布式架构高并发高可用微服务电商项目实战架构
Java 开发环境:idea https://www.jianshu.com/p/7a824fea1ce7 从无到有构建大型电商微服务架构三个阶段SpringBoot+SpringCloud+Solr ...
- 清晰架构(Clean Architecture)的Go微服务: 程序容器(Application Container)
清晰架构(Clean Architecture)的一个理念是隔离程序的框架,使框架不会接管你的应用程序,而是由你决定何时何地使用它们.在本程序中,我特意不在开始时使用任何框架,因此我可以更好地控制程序 ...
- 通俗地理解面向服务的架构(SOA)以及微服务之间的关系
SOA是一种软件的应用架构方法,它基于面向对象,但又不是面向对象,整体上是面向服务的架构.SOA由精确的服务定义.松散的构件服务组成,以及业务流程调用等多个方面形成的一整套架构方法. 这话是不是听起来 ...
- Re:从0开始的微服务架构--(二)快速快速体验微服务架构?--转
原文地址:https://mp.weixin.qq.com/s/QO1QDQWnjHZp8EvGDrxZvw 这是专题的第二篇文章,看看如何搭建一个简单模式的微服务架构. 记得好久之前看到一个大牛说过 ...
- 【分布式微服务企业快速架构】SpringCloud分布式、微服务、云架构快速开发平台源码
鸿鹄云架构[系统管理平台]是一个大型 企业.分布式.微服务.云架构的JavaEE体系快速研发平台,基于 模块化.微服务化.原子化.热部署的设计思想,使用成熟领先的无商业限制的主流开源技术 (Sprin ...
- 清晰架构(Clean Architecture)的Go微服务: 设计原则
我最近写了一个Go微服务应用程序,这个程序的设计来自三个灵感: 清晰架构"Clean Architecture"¹ and SOLID (面向对象设计)² 设计 原则³ Sprin ...
- 清晰架构(Clean Architecture)的Go微服务: 编码风格
编码风格在编程中是一个相对乏味的主题,但是合适的编码风格对一个有效的程序员是至关重要的. 它有三个组成部分: 程序结构 ( application layout) 编码规则或风格 命名约定 我已经在清 ...
- 清晰架构(Clean Architecture)的Go微服务
我用Go和gRPC创建了一个微服务项目,并试图找出最好的程序结构,它可以作为我其他项目的模板.我还将程序设计和编程的最佳实践应用于Go Microservice程序,例如清晰架构(Clean Arch ...
随机推荐
- linux 6.5操作系统建立
VM上redhat enterprise linux6 ---> 使用仅主机模式网络连接 开始安装: ——>是否检查镜像:skip ——>选择语言: 中文/英文 ——>存储设备 ...
- ArcGIS 问题汇总
1.Arcgis10.4出现Manager打不开的情况 解决方法: 1.检查进程中是否有占用4000以及6080端口的进程,如果有关闭 2.检查进程中是否有javaw.exe这个进程,如果有就把他结束 ...
- 【论文阅读】Deep Clustering for Unsupervised Learning of Visual Features
文章:Deep Clustering for Unsupervised Learning of Visual Features 作者:Mathilde Caron, Piotr Bojanowski, ...
- 2019-9-25:渗透测试,基础学习,初识Hydra,BP爆破密码
一,使用Hydra爆破ubuntu的SSH服务 输入命令,hydra -l root -P password.txt 192.168.20.128 ssh Hydra工具,基本参数说明 -l:指定用户 ...
- 如何理解Nginx, WSGI, Flask(Django)之间的关系
如何理解Nginx, WSGI, Flask(Django)之间的关系 值得指出的是,WSGI 是一种协议,需要区分几个相近的名词: uwsgi 同 wsgi 一样也是一种协议,uWSGI服务器正是使 ...
- 手机端web(iPad)页面自适应js
有关编写手机页面(ipad页面)自适应的方法有很多,比如:bootstrap,rem等等.下面分享给大家一个js控制viewPort视区自适应缩放的方法(我给它命名为phone.js): 将phone ...
- 相关性不一定等于因果性:从 Yule-Simpson’s Paradox 讲起
1. 两件事伴随发生,不代表他们之间有因果关系 - 从一些荒诞相关性案例说起 在日常生活和数据分析中,我们可以得到大量相关性的结论,例如: 输入X变量,有98%置信度得到Y变量 只要努力,就能成功 只 ...
- WSGI到底是什么?
在用Python Web开发时经常会遇到WSGI,所以WSGI到底是什么呢?本文我们一起来揭开WSGI神秘的面纱! 先来看一下WSGI的介绍: 全称Python Web Server Gateway ...
- idea中自定义快捷键
idea中自定义快捷键 在开发的过程中,总要写一些重复的代码,那么使用自定义快捷键,可以将这些经常用到的代码,自动生成. 1.在idea工具中点击File-->Settings 2.在弹出来的界 ...
- 清理Mac
mac在更新系统以及Xcode时总是报磁盘空间不足.于是下定决心来清理一下. 1.首先点击管理,清理下大文件,以及倾倒垃圾篓. 2.下载mac清理工具,清理一些缓存文件. 3.查看下文件中占用最大的文 ...