“单应用”的概念其实是相对于”微服务”来讲的。”微服务”项目往往单个服务逻辑比较简单,不会有过于复杂的代码设计。本文使用 Focus聚焦社区 项目作为示例,主要介绍如何使用GoFrame
开发框架设计包含多个系统的”单应用”项目。在开始之前,建议您先了解一下GoFrame
对于开发业务型项目的一些基础设计介绍:
本文是介绍的单应用下多系统的复杂设计,如果您对此比较感兴趣,欢迎往下阅读。如果您主要接触的微服务开发场景,可选择忽略,后续会有其他的设计文章对微服务工程这块做专门的介绍。
一、项目结构
包含多个系统的单应用目录结构会不太一样,主要由于不同的系统有不同的功能逻辑,也有相同的功能逻辑。我们需要将不同的功能逻辑充分地进行解耦,互不干扰;而相同的功能抽离出来,便于复用。还是高内聚,低耦合思想。
├── app
│ ├── dao
│ ├── model
│ ├── shared
│ └── system
│ ├── admin
│ │ └── internal
│ └── index
│ └── internal
│ ├── api
│ ├── define
│ └── service
├── config
├── document
├── library
├── packed
├── public
├── template
├── upload
├── Dockerfile
├── go.mod
└── main.go
目录/文件名称 | 说明 | 描述 |
---|---|---|
app | 业务逻辑层 | 所有的业务逻辑存放目录。 |
- dao | 数据访问 | 数据库的访问操作,仅包含最基础的数据库CURD方法。 |
- model | 结构模型 | 数据相关的实体结构定义,以及一些系统间通用的数据结构定义。 |
- | 通用逻辑 | 多系统间可复用的、通用的service 功能逻辑。 |
- system | 系统模块 | 内部可能包含多个子系统,不同子系统之间资源相互隔离。 |
- index | 前端页面 | 子系统,前端页面。该名称index 仅作示例。 |
- internal | 内部模块 | 系统内部模块,仅供当前内部系统调用,无法在系统间共享。 |
- api | 业务接口 | 当前系统内部接收/解析用户输入参数的入口/接口层 |
- define | 结构定义 | 当前系统内部的输入、输出数据结构定义。 |
- service | 逻辑封装 | 当前系统内部业务逻辑封装,实现特定的业务需求。 |
config | 配置管理 | 所有的配置文件存放目录。 |
docker | 镜像文件 | Docker 镜像相关依赖文件,脚本文件等等。 |
document | 项目文档 | Documentation 项目文档,如: 设计文档、帮助文档等等。 |
library | 公共库包 | 公共的功能封装包,往往不包含业务需求实现。 |
packed | 打包目录 | 资源文件打包的Go 文件存放在这里,boot 包初始化时自动调用。 |
public | 静态目录 | 仅有该目录下的文件才能对外提供静态服务访问。 |
template | 模板文件 | 模板文件存放的目录。仅当需要使用模板引擎的场景下有用。 |
Dockerfile | 镜像描述 | 云原生时代用于编译生成Docker 镜像的描述文件。 |
go.mod | 依赖管理 | 使用Go Module 包管理的依赖描述文件。 |
main.go | 入口文件 | 程序入口文件。 |
可以看到,基本的项目结构和GoFrame
官方提供的基础项目结构差别不大,主要在app
目录下的目录结构有一些差别。比较常见的单应用多系统是这样的:一个前台系统,一个后台系统。例如:
图1. 单应用多系统项目结构
二、复用模块
数据访问 - dao
可以看到处于外层的dao
和model
都是可被多个业务系统复用,这应该能够很好理解,这两块本身就是耦合最低的功能模块。dao
模块负责数据访问管理,对于整个应用来讲,数据是通用的,可以被多个业务系统访问。
通用结构 - model
但是需要注意的是,此时的model
中仅包含通用的数据结构定义,例如数据库实体对象定义、共享数据结构定义。例如,以下图例model
中的captcha
、context
、view
都是共享数据结构;而其他的模块均是数据库实体数据结构封装。
图2. 可复用model
通用数据结构模块
通用逻辑 - shared
shared
模块存放的是多系统间可复用的service
功能逻辑,由于service
这个名称被使用到业务系统中,因此外部相同职责的模块名称尽量不要重复冲突以便更好维护,所以叫做shared
。在下图的示例中,可以看到,我们整个单应用中,多个系统间共用了context
上下文的功能逻辑。
图3. 可复用的shared
功能逻辑
三、业务系统
在这个示例中,包含了两个业务系统index
前台系统和admin
后台系统,两个业务系统在代码设计上相互独立,没有耦合。可以看到,每个业务系统内部都使用internal
包将自身的业务逻辑包含了起来,目的是使得内部的功能模块不对外公开,使得整个项目在模块引用的时候更加简洁。
图4. 多业务系统代码结构示例
每个业务系统下均有api
、define
、service
三层代码模块,每个模块均负责当前业务系统下对应的职责管理。
业务接口 - api
api
模块负责当前业务系统的请求输入与输出,包括对输入参数的过滤、转换、校验,对输出数据结构的维护,并调用service
实现业务逻辑处理。
结构定义 - define
define
模块负责当前业务系统的数据结构定义,包括自定义的数据对象、输入输出数据结构等定义,其功能类似于GoFrame
项目结构中的model
,但是不包含数据库实体对象数据结构定义,而数据库实体对象数据结构定义由外层的model
负责。
业务逻辑 - service
service
模块负责当前业务系统的业务逻辑实现以及功能封装。
系统初始化
可以看到我们整个项目结构中,已经不存在GoFrame
框架项目结构推荐的boot
与router
模块。因为在多系统并存的情况下,无法执行统一的初始化和路由注册,初始化和路由注册交给各个业务系统自行维护,这样的目的也是为了尽可能地实现低耦合设计。例如,前台系统的初始化即路由注册在这里管理:
图5. 业务系统初始化即路由注册
四、应用初始化
应用初始化主要负责一些通用的初始化设置,并调用各个业务系统的初始化方法实现系统的初始化。随后启动应用。
图6. 应用初始化即启用
Content Menu