
这里讨论的是不用框架只用工具库( Toolkit )的情况,否则组织形式只能按照框架的规定,也就没什么讨论空间了。
其实同样的思路还可以用来组织具体的业务代码,整合第三方服务等不少场景。 这里先讲个大概,等过年回家之后再整理篇完整的博客。
入口部分主要是声明退出方式,然后开始初始化流程。
// cmd/app/main.go package main func main() { // 在第一次收到 SIG_INT ,SIG_TERM 时结束 Context , // 第二次收到时 os.Exit() ctx, cancel := module.GracefulContext() defer cancel() // 执行业务逻辑 if err := business.Run(ctx); err != nil { log.Fatalf("Error exit: %s", err) } } 组装第一层程序结构,主要是:命令行,外部依赖,端口监听。 这里以常见的 HTTP 服务器为例。
// business/app.go package business type App struct { module.CLI // 命令行 module.SQLite // 数据库 module.HTTP // HTTP 服务 } func Run(ctx context.Context) (err error) { var app App return module.Run(ctx, &app) } module.Run() 会依次初始化 App 中声明的每个 module ,然后运行所有实现了 Run() 的 module 。
初始化的大致流程为:module1.PreInit() -> module1.Init() -> module1.PostInit() -> module2.PreInit() -> ...
至于模块如何声明,就以 module.SQLite 为例:其中 Init() 和 Close() 都是 module 的可选接口。 module.HTTP 还需要实现 Run()。
// internal/module/sqlite.go package module type SQLite struct { DSN string DB *sql.DB } func (m *SQLite) Init(ctx context.Context) (err error) { if m.DB, err = sql.Open("sqlite3", m.DSN); err != nil { return } ctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() return m.DB.PingContext(ctx) } func (m *SQLite) Close() error { return m.DB.Close() } module.HTTP 和 module.CLI 代码比较长就不贴了,完整的 Demo 在 Github: https://github.com/GotaX/module-demo
有疑问或者想分享下自己是如何组织代码的也欢迎留言讨论。
1 dacapoday 2023-01-17 12:43:24 +08:00 |