Skip to end of metadata
Go to start of metadata

"单应用"的概念其实是相对于"微服务"来讲的。"微服务"项目往往单个服务逻辑比较简单,不会有过于复杂的代码设计。本文使用 Focus聚焦社区 项目作为示例,主要介绍如何使用GoFrame开发框架设计包含多个系统的"单应用"项目。在开始之前,建议您先了解一下GoFrame对于开发业务型项目的一些基础设计介绍:

本文是介绍的单应用下多系统的复杂设计,如果您对此比较感兴趣,欢迎往下阅读。如果您主要接触的微服务开发场景,可选择忽略,后续会有其他的设计文章对微服务工程这块做专门的介绍。

针对了解MonoRepo的同学。本文介绍的单应用多系统MonoRepo大仓管理模式不一样,两者差别在于:

"单应用多系统"设计针对的是同一个应用有较强业务逻辑耦合(业务和代码直接耦合)的子项目的管理,往往会共用数据库Dao&Model、业务逻辑封装模块等,不共用的部分单独提出来作为system模块进行管理。这种设计往往是带有前后台逻辑的系统中比较常见。

MonoRepo是一种项目管理模式,与业务无关,虽然MonoRepo的项目都处于一个仓库下,但是多个项目往往尽量减少业务逻辑上耦合(耦合的部分应当使用接口通信)。关于MonoRepo未来会有独立的文章进行介绍。

一、项目结构

包含多个系统的单应用目录结构会不太一样,主要由于不同的系统有不同的功能逻辑,也有相同的功能逻辑。我们需要将不同的功能逻辑充分地进行解耦,互不干扰;而相同的功能抽离出来,便于复用。还是高内聚,低耦合思想。

├── 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结构模型数据相关的实体结构定义,以及一些系统间通用的数据结构定义。

-  shared

通用逻辑多系统间可复用的、通用的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

可以看到处于外层的daomodel都是可被多个业务系统复用,这应该能够很好理解,这两块本身就是耦合最低的功能模块。dao模块负责数据访问管理,对于整个应用来讲,数据是通用的,可以被多个业务系统访问。

通用结构 - model

但是需要注意的是,此时的model中仅包含通用的数据结构定义,例如数据库实体对象定义、共享数据结构定义。例如,以下图例model中的captchacontextview都是共享数据结构;而其他的模块均是数据库实体数据结构封装。

图2. 可复用model通用数据结构模块

通用逻辑 - shared

shared模块存放的是多系统间可复用的service功能逻辑,由于service这个名称被使用到业务系统中,因此外部相同职责的模块名称尽量不要重复冲突以便更好维护,所以叫做shared。在下图的示例中,可以看到,我们整个单应用中,多个系统间共用了context上下文的功能逻辑。

图3. 可复用的shared功能逻辑

三、业务系统

在这个示例中,包含了两个业务系统index前台系统和admin后台系统,两个业务系统在代码设计上相互独立,没有耦合。可以看到,每个业务系统内部都使用internal包将自身的业务逻辑包含了起来,目的是使得内部的功能模块不对外公开,使得整个项目在模块引用的时候更加简洁。

图4. 多业务系统代码结构示例

每个业务系统下均有apidefineservice三层代码模块,每个模块均负责当前业务系统下对应的职责管理。

业务接口 - api

api模块负责当前业务系统的请求输入与输出,包括对输入参数的过滤、转换、校验,对输出数据结构的维护,并调用service实现业务逻辑处理。

结构定义 - define

define模块负责当前业务系统的数据结构定义,包括自定义的数据对象、输入输出数据结构等定义,其功能类似于GoFrame项目结构中的model,但是不包含数据库实体对象数据结构定义,而数据库实体对象数据结构定义由外层的model负责。

业务逻辑 - service

service模块负责当前业务系统的业务逻辑实现以及功能封装。

系统初始化

可以看到我们整个项目结构中,已经不存在GoFrame框架项目结构推荐的bootrouter模块。因为在多系统并存的情况下,无法执行统一的初始化和路由注册,初始化和路由注册交给各个业务系统自行维护,这样的目的也是为了尽可能地实现低耦合设计。例如,前台系统的初始化即路由注册在这里管理:

图5. 业务系统初始化即路由注册

四、应用初始化

应用初始化主要负责一些通用的初始化设置,并调用各个业务系统的初始化方法实现系统的初始化。随后启动应用。

图6. 应用初始化即启用









Content Menu

  • No labels

10 Comments

  1. 你这个文档写得非常详细,我学了很多语法就是不知道怎么构建项目,对我这种没有工程化项目经验的新人来说,实在是太好了,值得我反复阅读,太赞了。

  2. 比如后端,admin下面功能太多,里面的模块如何划分,都塞到一个文件夹里面感觉有点乱.

    1. 代码分层之后,逻辑较重的部分就是service层代码了。通过对象化的逻辑封装之后,每块业务逻辑仅绑定和维护到对应的对象上,并在多人协作时通过不同的文件进行区分,是很好维护的。service层的逻辑对外暴露的时候只是实例化的对象,比较简洁易用。具体可以看看 对象封装设计 章节。

  3. index前台系统和admin后台系统


    您好,这里的前台系统, 指的是对C端用户的api接口代码?

    admin后台系统指的是  公司内部的cms系统?

    1. 一般情况下,前台系统指的业务操作部分。后台系统指的是一些配置和管理部分。 就拿酒店管理系统来说,预定房间、退房等业务属于前台业务,员工管理、参数设置等属于后台系统。前端开发的时候,有时候会做两套系统,分别调用接口。这是我个人理解。

  4. 你好,想请问一下,我们公司的前台应用和后台应用绑定了不同的域名(因为前台平台需要交付客户使用,后台系统我们自己管理使用),这种业务场景是不是不适用单应用多系统设计,而是依然开发单独的前台应用和后台应用较好

    1. 前台系统如果交付的是源码的话,那最好是独立项目管理比较好。当然如果采用单应用多系统设计,你也可以再交付的时候把一些敏感代码剔除掉,例如直接提出system/admin的代码,项目也是可以直接运行的。你们自己决定。

  5. 个人觉得多系统是个伪需求,多系统就应该是多个单应用,就算有共用的代码部分,可以复制代码,对于golang这个语言来说冗余并不是问题,没必要整合在一起.框架给予的太多,就会造成选择性很多,选择性多了,就造成了框架使用的复杂性,也就增加了框架使用的难度性.

    1. 补充: 从多应用可以看出 dao,model,shared这三个目录都属于公共部分,与app目录的内容应该是分离才对,而app目录内应该直接就是admin和index..另外应用部分既然是多应用,就应该是独立端口,独立线程或进程..再有就是路由和配置,既然是多应用,独立路由,独立配置也是理所应当的,那么这两个东西就应该独立到每个应用的目录中去是不是..最后是编译部署,既然是多应用,就应该可以独立编译出多个应用,可以独立进行部署..以上只是个人不成熟的浅见.

    2. 我觉得公共的代码,比如model之类的,是否可以做成一个私有的库,供多个系统来调用?目前的我司的系统是这么做的,不知道有没有什么缺点