前言

Go 语言已经广泛被应用于服务端开发,成为了构建高效、可靠服务端应用程序的首选语言。因此,精通如何搭建一个 Go 服务端项目,是每一位 Go 开发者必备的技能。

一个良好的服务端项目,其核心在于其项目架构的设计。一个优秀的项目架构能够帮助我们有序地组织代码,极大地提升代码的可维护性和可读性,同时也能有效地提高开发效率。

在接下来的文章中,我将简单探讨一下 Go 语言在 Web 服务端开发中常用的一种项目架构。

go-web 服务项目结构

为什么要进行项目分层

项目代码分层是一种常见的软件开发实践,主要有以下几个理由:

  1. 可维护性:当代码分层清晰时,维护工作相对容易进行。每个层次都有明确的职责,使得定位和修复问题更为简单。此外,如果需要修改某个功能或行为,只需要在相应的层进行修改,而不会影响到其他层。
  2. 复用性:代码分层可以提高代码的复用性。例如,数据访问层的代码通常可以在多个场景中重复使用,无论是在不同的业务逻辑中,还是在不同的项目中。
  3. 可测试性:代码分层有助于单元测试和集成测试。每一层都可以独立地进行测试,这使得测试工作更为简单和可控。
  4. 解耦:分层设计有助于降低各部分代码之间的耦合度。每一层都只依赖于其下一层,使得各层之间的依赖关系清晰明了。
  5. 灵活性:当需要更改某一层的实现时,分层设计可以确保其他层不受影响。例如,如果你想更换数据库或者更改业务逻辑,只需要对相关层进行修改,而不会影响到其他层。

简单通过一个例子来说明一下为什么要进行项目分层:

比如说,目前这个项目有一个登录的业务,项目内部维护了一套用户的账户信息,同时还可以使用第三方账号登录。用户登录成功后,需要把登录会话信息保存到数据库。

如果不分层的话,我们直接在一个文件里完成了所有的功能,就会出现以下情况:

  1. 代码文件冗长,可阅读性差
  2. 单元测试难以进行
  3. 拓展性低,可维护性差,后续若是有接入其他三方账号登录的需求时,需要测试整个流程
  4. 项目代码无法复用,比如说有另外的注册接口,注册成功后自动添加一条登录会话到数据库,会话入库的代码需要再写一次
  5. 假如项目更改了数据库,需要全部推翻重写

对于项目分层的易测试性,可以阅读我的下一篇文章,主要讲的就是单元测试,其中会讲到如何测试本篇文章中所说的项目架构。

如何分层

将项目分成4层,分别为 controller、 service、 repository 以及 model。

项目分层示意图

每一层只负责自己的任务,根据调用链路,上层的服务可以调用下层的服务,可以跨层调用,但是下层服务禁止调用上层。

这么设计的目的是避免循环导包的出现,且代码结构更加清晰明了。

根据该项目分层,我们在来聊聊如何优化上文中的登录功能。

首先,controller 层负责处理与客户端的交互,因此,在 controller 层需要做的就是设计接口路由、完成请求参数的解析和响应数据的封装和格式化,业务逻辑则调用 service 层来完成。

当代码来到 service 层,我们需要完成登录模块的逻辑,需要实现的有:

  1. 通过项目账号登录,该逻辑为数据库交互,需要调用 repository 层的方法,访问用户数据,进行帐密匹配;
  2. 通过第三方账户登录,一般是通过第三方 SDK 或 API 来实现;
  3. 登录态入库,为数据库交互,调用 repository 层的方法实现;

在这一层,我们实现了所有的业务逻辑,但是对数据的存储是如何实现是不关心的。

实现数据存储的,即是我们的 repository 层,该层需要实现的是查询用户的方法和新增登录态的方法。在这一层,我们需要调用各个数据库的驱动,完成数据的 CRUD。

而在 repository 层与数据库服务的交互间进行关联的,就是我们的 model 层了,model 层主要实现的就是与数据库实体之间的映射,将数据库结构映照成 go 结构,该结构是可以在整个调用链之间传递的。

可以看到,分层之后,项目的代码结构就很清晰了,能很好的解决上文中提到的几个问题:

  1. 可阅读性良好,能够根据对应的功能跳转到目标文件中阅读代码
  2. 将代码拆分后,能够根据每个部分进行单元测试,比如说测试数据入库、测试登录逻辑
  3. 项目代码能够复用,比如说 service 层的注册模块可以调用 repository 层的用户查询方法,查询用户是否已注册

此时,我们就会发现,仍然有两个问题是分层后没有解决的。这时候就可以引入我们程序员之间流传的一句名言了:“没有什么问题是加一层解决不了的,有的话就再加一层”。

对于对接多个第三方登录以及更改项目数据库的问题,我们可以加一层抽象层来实现。将登录抽象为一个接口,底层通过各自的结构体对象来实现登录接口,即可实现更多第三方账户的接入问题。同样的,数据库的查询用户和新增登录态,也可以抽象为一个接口,底层通过不同的数据库结构,来实现这个接口。service 层使用的永远都是接口对象,而不是接口的实现类对象。

项目文件结构

在讨论了项目的架构之后,我们现在转向另一个重要的话题:如何在项目中组织和存放分层的代码。在这个问题上,每个人可能都有自己的习惯和首选方式。以下分享的目录结构并非唯一的解决方案,仅供参考。

代码目录结构

结语

本篇文章简单介绍了 go 语言在 Web 服务端开发中的一种常用项目架构,解释了一个优秀的项目架构对于代码组织、可维护性、可读性以及开发效率的重要性。

同时我们需要记住的是,没有任何架构是适用于所有项目的。在实际开发过程中,我们需要根据项目的需求和规模,选择合适的架构。只有这样,我们才能更好地满足项目的需求,打造出真正优秀的服务端应用。