快速开始
这里为您提供了一些项目例子,供您快速开始。理论上,只要保证 app 包能正常运行您定义的 模块
,那么任何目录结构都是可行的。
http服务
克隆代码到本地
git clone -b step1 --single-branch git@github.com:zeddy-go/quickstart.git
我们可以看到如下目录结构
.
├── conf //配置
│ ├── config.go
│ └── config.yaml
├── go.mod
├── go.sum
├── main.go //入口文件
└── module //模块
└── user //用户模块
├── iface //接口层
│ └── http //http接口
│ └── user.go //user handler
└── module.go //模块入口
我们使用embed的方式来加载配置,然后通过环境变量来对配置做改动。 这种方式在容器化部署的场景下非常方便。
我们引用了 ginx 模块和 user 模块。 ginx 模块为框架内置模块,用于处理 http 请求,底层使用 gin 。
TIP
您完全可以使用任何您喜欢的http库编写自己的http处理模块。或者自己造轮子💯。
user 模块则是我们自己的业务模块。 user 模块实现了一个 handler 方法,当接收到请求时,该方法会输出 hello xxxx! 的JSON响应(module/user/iface/http/user.go)。 接着,它向 ginx 模块注册了一个路由,以接收对应的请求(module/user/module.go:30)。
在这里您会注意到 container.Invoke 方法。没错,框架使用了依赖注入
。 (我们正尝试在开发效率和执行效率之间找到平衡。) 而对象的实例化方法则是在上面的Init方法中进行绑定(module/user/module.go:21)。
TIP
请在Init方法中绑定实例化方法,在Boot方法中或其他地方使用他们。
启动服务 go run .
,然后访问 http://localhost:8080/hello?username=zed 。
更完整的例子
克隆代码到本地
git clone -b step2 --single-branch git@github.com:zeddy-go/quickstart.git
在这个例子中,目录结构如下:
.
├── config //配置
│ ├── config.go
│ └── config.yaml
├── docker-compose.yaml //这个例子使用docker来启动MySQL数据库
├── go.mod
├── go.sum
├── main.go //入口文件
└── module
└── user
├── domain //领域层
│ ├── contract.go //一些接口
│ ├── svc //服务
│ │ └── user.go
│ └── user.go //领域实体
├── iface //接口层
│ └── http
│ ├── payload.go
│ └── user.go //用户 handler
├── infra
│ ├── migration //迁移
│ │ ├── 20240408063653_create_users_table.down.sql
│ │ ├── 20240408063653_create_users_table.up.sql
│ │ └── migration.go
│ ├── model //数据模型
│ │ └── user.go
│ ├── repo //仓库
│ │ └── user.go
│ └── seed //数据填充
│ └── user.go
└── module.go //模块入口
数据操作相关
在这个例子中数据模型与领域实体相似,但在实际项目中,一个领域实体也可能由多个数据模型的数据组成。 业务逻辑只使用领域实体,所以在通过仓库对数据做操作时,仓库会帮我们进行双向转换。
TIP
仓库内置了简单的转换逻辑,但无法满足复杂情况。这时就需要自定义转换逻辑。
数据迁移
框架内置的 migration 模块使用 github.com/golang-migrate/migrate 包实现迁移。 这同样需要在模块入口中注册(module/user/module.go:41)。程序启动后,数据表会自动创建到数据库。
数据填充
框架内置了数据填充,原理很简单,就是利用容器执行函数。 这同样需要在模块入口中注册(module/user/module.go:42)。 程序启动后,数据会自动填充到数据库。
user handler
在 user handler 中,我们添加了两个方法,一个是创建用户的方法,一个是获取用户信息的方法。 与第一个例子一样,我们在 module.go 中也新注册了两个路由。
您会发现这两个新方法与第一个例子中的方法的返回值数量不一样。 这正是框架的另一个特性,handler函数的可变参数和返回值。 框架允许您为每个handler函数设计自己的参数和返回值。 对于参数,框架会遍历参数列表,查找容器中的对象(依赖注入)或者解析请求的参数。 对于返回值,框架会遍历返回值列表,来决定如何返回响应。
添加grpc服务
克隆代码到本地
git clone -b step3 --single-branch git@github.com:zeddy-go/quickstart.git
我们在上面的项目的基础上添加grpc服务,这样我们的服务不仅能服务http也能同时服务grpc,还能展示代码重用的特性。 当然,如果您的项目只需要基于grpc,您完全可以不用理会http相关的代码。
项目目录结构如下:
.
├── conf
│ ├── config.go
│ └── config.yaml
├── docker-compose.yaml
├── go.mod
├── go.sum
├── main.go
├── module
│ └── user
│ ├── domain
│ │ ├── contract.go
│ │ ├── svc
│ │ │ └── user.go
│ │ └── user.go
│ ├── iface
│ │ ├── grpc
│ │ │ └── user.go //grpc接口实现
│ │ └── http
│ │ ├── payload.go
│ │ └── user.go
│ ├── infra
│ │ ├── migration
│ │ │ ├── 20240408063653_create_users_table.down.sql
│ │ │ ├── 20240408063653_create_users_table.up.sql
│ │ │ └── migration.go
│ │ ├── model
│ │ │ └── user.go
│ │ ├── repo
│ │ │ └── user.go
│ │ └── seed
│ │ └── user.go
│ └── module.go
├── pb //pb文件
│ ├── user_grpc.pb.go
│ └── user.pb.go
├── readme.md
└── user.proto //proto文件
在 main.go 文件中,我们添加内置的 grpc 模块(main.go:21)。 而注册我们自己的服务,只需在 module/user/module.go 文件中从容器中拿到已实例化好的 *grpc.Server 即可(module/user/module.go:44,module/user/module.go:65)。另外,您可以看到,在grpc接口实现文件里面,我们重用了 userService
。
TIP
您完全可以将您的常用代码以模块
的形式固化起来。
容器中的每个对象默认为单例模式,得益于常驻内存,已实例化的对象将不再重复实例化,这对执行效率来说是一件好事。
TIP
github.com/zeddy-go/zeddy/errx 包默默的收集了错误栈,您可以使用这样的代码来打印它 fmt.Printf("%#v", err) 。 并且,这些信息在经过grpc传输后也不会丢失。