我使用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微服务: 程序结构的更多相关文章

  1. (转)微服务架构 互联网保险O2O平台微服务架构设计

    http://www.cnblogs.com/Leo_wl/p/5049722.html 微服务架构 互联网保险O2O平台微服务架构设计 关于架构,笔者认为并不是越复杂越好,而是相反,简单就是硬道理也 ...

  2. Java 18套JAVA企业级大型项目实战分布式架构高并发高可用微服务电商项目实战架构

    Java 开发环境:idea https://www.jianshu.com/p/7a824fea1ce7 从无到有构建大型电商微服务架构三个阶段SpringBoot+SpringCloud+Solr ...

  3. 清晰架构(Clean Architecture)的Go微服务: 程序容器(Application Container)

    清晰架构(Clean Architecture)的一个理念是隔离程序的框架,使框架不会接管你的应用程序,而是由你决定何时何地使用它们.在本程序中,我特意不在开始时使用任何框架,因此我可以更好地控制程序 ...

  4. 通俗地理解面向服务的架构(SOA)以及微服务之间的关系

    SOA是一种软件的应用架构方法,它基于面向对象,但又不是面向对象,整体上是面向服务的架构.SOA由精确的服务定义.松散的构件服务组成,以及业务流程调用等多个方面形成的一整套架构方法. 这话是不是听起来 ...

  5. Re:从0开始的微服务架构--(二)快速快速体验微服务架构?--转

    原文地址:https://mp.weixin.qq.com/s/QO1QDQWnjHZp8EvGDrxZvw 这是专题的第二篇文章,看看如何搭建一个简单模式的微服务架构. 记得好久之前看到一个大牛说过 ...

  6. 【分布式微服务企业快速架构】SpringCloud分布式、微服务、云架构快速开发平台源码

    鸿鹄云架构[系统管理平台]是一个大型 企业.分布式.微服务.云架构的JavaEE体系快速研发平台,基于 模块化.微服务化.原子化.热部署的设计思想,使用成熟领先的无商业限制的主流开源技术 (Sprin ...

  7. 清晰架构(Clean Architecture)的Go微服务: 设计原则

    我最近写了一个Go微服务应用程序,这个程序的设计来自三个灵感: 清晰架构"Clean Architecture"¹ and SOLID (面向对象设计)² 设计 原则³ Sprin ...

  8. 清晰架构(Clean Architecture)的Go微服务: 编码风格

    编码风格在编程中是一个相对乏味的主题,但是合适的编码风格对一个有效的程序员是至关重要的. 它有三个组成部分: 程序结构 ( application layout) 编码规则或风格 命名约定 我已经在清 ...

  9. 清晰架构(Clean Architecture)的Go微服务

    我用Go和gRPC创建了一个微服务项目,并试图找出最好的程序结构,它可以作为我其他项目的模板.我还将程序设计和编程的最佳实践应用于Go Microservice程序,例如清晰架构(Clean Arch ...

随机推荐

  1. 以太网驱动的流程浅析(一)-Ifconfig主要流程【原创】

    以太网驱动的流程浅析(一)-Ifconfig主要流程 Author:张昺华 Email:920052390@qq.com Time:2019年3月23日星期六 此文也在我的个人公众号以及<Lin ...

  2. Java基础知识总结之类的集合

    Java集合概述 1.集合类也叫作容器类.它的功能相当于一个容器.可以存储数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组). 2.Java的集合(容器),它是用来”装对象的“(实际上是 ...

  3. python 实现图片批量加入水印!pillow 入门实战!

    写文章的时候可以设置是否添加水印.可是,有些图片可能想加水印,有些不想加水印,该怎么办呢? 配置环境 python3 + pillow pip3 install pillow 引入库 from PIL ...

  4. /proc/cpuinfo文件解读(超易理解)

    在linux系统中,提供了/proc目录下文件,显示系统的软硬件信息.如果想了解系统中CPU的提供商和相关配置信息,则可以查/proc/cpuinfo.但是此文件输出项较多,不易理解.例如我们想获取, ...

  5. wincap linux部署

    1.4.1 linux下安装Winpcap a) 下载Winpcap的源码:https://www.winpcap.org/devel.htm b) 上传源码包“WpcapSrc_4_1_3.zip” ...

  6. Linux目录结构-上部

    第1章 目录结构 1.1 目录结构特点 倒挂的树状结构一切从根开始一切皆文件 1.2 目录结构 /bin            二进制文件  命令 /sbin           超级命令只有root ...

  7. Echarts自定义折线图例,增加选中功能

    用Echarts图表开发,原本的Echarts图例不一定能满足我们的视觉要求. 下面是Echarts 折线图自定义图例,图例checked选中,相应的折线线条会随之checked,其余未选中的图例对应 ...

  8. NFS介绍、服务端安装配置、NFS配置选项

    6月21日任务 14.1 NFS介绍14.2 NFS服务端安装配置14.3 NFS配置选项 14.1 NFS介绍 14.2 NFS服务端安装配置 1.首先需要2台机器,一台是服务端,一台是客户端,分别 ...

  9. python check excel 文件

    Use pip install openpyxl first Every simple example import openpyxl # 打开excel文件,获取工作簿对象 wb = openpyx ...

  10. windows虚拟机中DNS服务配置

    在linux虚拟机中进行DNS服务配置并进行正向解析反向解析我博客中已经写过,下面 我来介绍一下在windows虚拟机中DNS服务的配置使用. 1.打开一台windows虚拟机中服务器管理器——角色— ...