go 开发中,首先遇到的一个问题,应该怎么设计开发架构。一个好的架构可以在后续的项目开发中能够更好的使用项目的推进,同时能够让团队的配合更加的顺利。

一个简洁的架构应该具有如下几个特点:

  • 不依赖框架:好的架构应当是独立的,不依赖框架可扩展的
  • 不依赖 UI:UI 应该很容易修改,而不需要改业务部分代码
  • 独立于数据库:不绑定数据库,可以在不改动业务代码的情况下,更换其他数据库
  • 可测试的: 能够比较方便测试用例的编写

下面目录结构引自 《go 整洁模板》

├─cmd 应用入口
│  └─app
├─config
├─docs // 存放文档
├─internal
│  ├─app
│  ├─controller // 控制器
│  │  ├─amqp_rpc
│  │  └─http
│  │      └─v1
│  ├─entity // 实体层
│  ├─middleware // 中间件
│  └─usecase
│      ├─repo // 数据库操作
│      └─webapi // RESTful API
├─migrations
├─pkg //以被外部程序安全导入的包
│  ├─crypto
│  ├─httpresponse
│  ├─httpserver
│  ├─logger
│  ├─mysql
│  ├─postgres
│  ├─rabbitmq
│  └─redis

分层介绍

层级调用是通过外层->内层,内层是不知道外层的存在的。在内层代码中,不应该引用外层声明的:变量、类、方法,结构体。

这样做可以保证内层的独立,如果外层改变,不会对内层造成影响。

这里层级有四层分别为:实体(Entities)用例(Use Cases)接口适配器(Interface Adapters)框架和驱动因素(Frameworks and Drivers)

结构的层级并不是固定的,可以根据自身需求在此基础上进行增减。

实体(Entities)internal/entity

实体类似于 MVC 中的 M 层,定义了应用程序的业务对象,实体除了定义结构体外,还可以有相关方法(参数校验)。

用例(usecase) internal/usecase

业务逻辑.

  • 方法按应用领域分组(在共同的基础上)
  • 每个组都有自己的结构
  • 一个文件对应一个结构 Repositories、webapi、rpc等业务逻辑结构被注入到业务逻辑结构中

接口适配器(Interface Adapters) internal/controller

该层级主要用于数据传递,以 API 调用为例,将用户传递的参数进行整理成用例(usecase)所需的数据格式,接收用例(usecase) 处理后返回的数据转换成响应的数据。

框架和驱动因素(Frameworks and Drivers)

最外层通常由框架和工具组成,如数据库、Redis、Web 框架等。一般来说,除了与向内的下一个环通信的粘合代码外,这一层不会涉及到代码的编写。

调用逻辑

层级调用应该是从外层->内层,以一个 HTTP 请求为例:

    HTTP > usecase
           usecase > repo
           usecase < repo
    HTTP < usecase

这里需要注意 usercase 不能直接调用 Postgres ,这样违反了依赖原则,使内层(usecase) 知道了外层 (Postgres) 的存在。

应该在 usercase 中定义接口(interface),在外层使用 Postgres 实现该接口。通过依赖注入的形式,将实现的接口注入到 usecase 中,然后 usecase 通过接口进行操作 Postgres。

如上图所示,usercase/Article 依赖 usecase/ArticleOpt 接口查询 Postgres。外层的 repo/Article 对 usecase/ArticleOpt 接口的实现。在初始化 usercase/Article 时,将 repo/Article 注入 usercase/Article ,usercase/Article 就可以通过注入的具体实现 (repo/Article) 完成 Postgres 查询。

总结

此架构模式通过依赖注入的方式来保证各个层级间的独立,不会因为某个层级的改动而影响其他层。

对于程序的扩展也是十分方便,由于层级的之间并不相互依赖而是依赖对应的接口,这样如果需要更换某个组件只需要完成对应接口的实现就可以完成组件的替换,无需修改业务代码和其他层级中的代码改动。

参考链接