Freedom 是一个用 Go 写 DDD 服务的框架。controller 只管收发,业务逻辑只认持久化接口,事务不用改函数签名。
请求生命周期、依赖注入、缓存、领域事件、CRUD 生成,这些框架先做掉。
生成项目后大概长这样:
myapp/
├── adapter/
│ ├── controller/ # 入站:HTTP
│ └── repository/ # 出站:持久化 / 上游接口
├── domain/
│ ├── aggregate/ # 聚合:CQS 的命令与查询
│ ├── entity/ # 实体:业务行为 + 领域事件
│ ├── event/ # 领域事件
│ ├── po/ # 持久化对象
│ └── vo/ # 值对象
├── infra/ # 基础设施
└── config/ # 配置
domain:实体、聚合、领域事件,业务规则都在这里。adapter:入站接 HTTP 和消息,出站做持久化和上游接口调用。infra:事务、缓存、HTTP 客户端这类东西,用的时候注入。
分层本身不新鲜。六边形架构、DDD、依赖倒置,大家讲了很多年。Freedom 把这些约定写进框架:注册、注入、生命周期,不用每个项目重新约定一遍。
每个 HTTP 请求对应一个 Worker。日志、链路信息、事务句柄、请求级缓存、标准 context 都挂在它上面。
Service 和 Infra 从对象池里取,Repository、Factory 每次请求新建。对象可以复用,状态不共享。不用自己清缓存,也不用担心两个请求共用一个实例。
在请求里开 goroutine,记得先调 Worker.DeferRecycle()。否则请求结束对象被回收,goroutine 里再用就晚了。
type UserController struct {
UserSev *domain.UserService
}
func (c *UserController) GetBy(id int) freedom.Result {
user, err := c.UserSev.Get(id)
return &infra.JSONResponse{Object: user, Error: err}
}Controller 里没有 new,也不用在 main 里手写一串对象再一个个塞进去。字段写上,框架按类型注入;UserService 里的 Repository 也一样。
GetBy(id int) 对应 GET /user/{id},参数也不用自己拆。
比如转账:
return cmd.tx.Execute(func() error {
cmd.fromAccount.Withdraw(cmd.amount)
cmd.toAccount.Deposit(cmd.amount)
if err := cmd.accountRepo.Save(cmd.fromAccount); err != nil {
return err
}
return cmd.accountRepo.Save(cmd.toAccount)
})Repository 还是那个 Repository,但拿到的 DB 已经换成事务里的。业务方法不用加 tx 参数,也不用把调用链一路改到底。上面两个 Save 要么都成功,要么都回滚。
func (u *User) ChangePassword(newPassword, oldPassword string) error {
if u.Password != oldPassword {
return errors.New("password error")
}
u.SetPassword(newPassword)
u.AddPubEvent(&event.ChangePassword{
UserID: u.ID,
NewPassword: newPassword,
OldPassword: oldPassword,
})
return nil
}实体只负责记录事件。什么时候投递、投给谁,由基础设施决定。直接在这里发 Kafka 也不是不行,但事务一滚,消息已经发出去了,数据就对不上。接到事务后面,提交了才发。
# MySQL
freedom new-po --dsn "root:密码@tcp(127.0.0.1:3306)/mydb?charset=utf8"
# PostgreSQL
freedom new-po --driver postgres -d "host=127.0.0.1 port=5432 user=root password=123456 dbname=mydb sslmode=disable"
# 或者给一份 JSON Schema
freedom new-po --json ./domain/po/schema.json生成的 PO 带 setter,改过哪些字段它自己记着。保存时只 update 这几个字段,不会整行覆盖。手写这些代码不算难,就是烦。
# 1. 安装 CLI
go install github.com/8treenet/freedom/freedom@latest
# 2. 生成项目
freedom new-project myapp
cd myapp
go mod tidy
# 3. 跑起来
go run main.go打开 http://127.0.0.1:8000,或者 curl 一下 /ping。
| 能力 | 内容 |
|---|---|
| 架构 | 六边形架构、DDD、CQS、聚合、实体、值对象、领域事件 |
| 运行时 | 请求级 Worker、依赖注入、对象池、服务定位器 |
| HTTP | Iris MVC、HTTP/2、H2C、AutoTLS、中间件 |
| 数据 | GORM、MySQL、PostgreSQL、Redis、两级实体缓存 |
| 消息 | Kafka 生产者/消费者、领域事件投递 |
| 治理 | Prometheus(HTTP / ORM / Client / Kafka)、Trace、请求日志、Recover |
| 工程 | 项目脚手架、PO/CRUD 生成、单元测试工具、优雅停机 |
| 配置 | TOML、YAML、环境变量指定配置目录 |
| 文档 | 你会看到什么 |
|---|---|
| 路由指南 | 路由、参数、控制器 |
| 服务指南 | 服务层如何编排业务 |
| PO 指南 | 持久化对象与数据库操作 |
| DDD 指南 | 实体、聚合、工厂、CQS |
| Worker 指南 | 请求运行时与对象生命周期 |
| HTTP 客户端指南 | 客户端、链路透传、超时控制 |
不想翻文档的话,直接跑例子:
| 示例 | 适合看什么 |
|---|---|
| base | 最小闭环,先跑通 |
| http2 | H2C 服务间调用、依赖倒置 |
| infra-example | 自定义 Infra、事务、Kafka、领域事件 |
| fshop | 电商全链路:购物车、订单、库存、事件 |
如果你在做:
- 业务不只是 CRUD 的系统
- 要长期维护、多人协作的 Go 服务
- 想把领域模型从数据表里拆出来的团队
Freedom 会比较合适。
如果你只是想尽快把接口发出去,Gin、Echo、Iris 都比 Freedom 省事。DDD 有成本,Freedom 不打算把这件事说得很轻松。
Freedom 不保证架构不出问题。它能做的是先把请求、事务、缓存、注入这些事接好,让你打开 domain 时,里面只有业务。
Issue、PR、文档修正都欢迎。用着顺手的话,点个 Star。
Apache 2.0 · Issues · Releases
