Kratos默认不内置MongoDB支持,因其坚持“按需加载”设计哲学,数据访问层完全由开发者自主选型组装,官方模板仅提供基础设施骨架,不强制绑定任何ODM或声明式接口。

Kratos 框架本身不内置 MongoDB 支持,必须手动集成驱动并管理生命周期;它不像 Spring Boot 那样有 spring-boot-starter-data-mongodb 这类开箱即用的模块。
为什么 Kratos 默认不带 MongoDB 集成
Kratos 的设计哲学是「按需加载」——所有数据访问层(data 层)完全由开发者自主选型和组装。官方模板默认只包含 gRPC/HTTP 服务骨架、日志、配置、错误处理等基础设施,数据库驱动(MySQL/PostgreSQL/MongoDB)全靠你显式引入、初始化、注入。
- 它不强制绑定任何 ORM 或 ODM,避免框架侵入业务数据模型
- 没有
MongoRepository这类声明式接口,所有操作都基于原生mongo-go-driver - 连接池、超时、重试、上下文传播都需要你自己在
data层封装
如何在 data 层正确初始化 *mongo.Client
别在 main.go 或 handler 里直接调用 mongo.Connect() —— 这会导致连接泄漏、无法统一管理、测试难 mock。标准做法是在 data 包中定义一个结构体,把 *mongo.Client 和 *mongo.Database 封装进去,并通过 Wire 或手动依赖注入传入 service。
- 使用
mongo.Connect(ctx, options.Client().ApplyURI(uri)),且必须设置context.WithTimeout防止启动卡死 - 连接字符串里建议显式指定数据库名,例如
mongodb://localhost:27017/myapp?maxPoolSize=20 - 务必在应用退出前调用
client.Disconnect(ctx),Kratos 的app.Run()支持优雅关闭钩子 - 不要复用全局变量存 client,Wire 注入能保证单例且可测试
CRUD 操作要绕开哪些坑
Go 官方驱动(mongo-go-driver)的 API 是泛型友好的,但容易忽略几个关键细节:
-
FindOne返回*mongo.SingleResult,必须调用.Decode(&v)才能取值,直接.Err()判断是否为空文档会误判(比如查不到时.Err()是mongo.ErrNoDocuments,不是nil) - 写入嵌套文档或数组时,字段名要用 struct tag
bson:"field_name",否则序列化后字段名变成 Go 风格(如CreatedAt→createdat) - 用
UpdateOne更新时,$set操作符必须包装成bson.M{"$set": bson.M{...}},不能直接传bson.M{...} - 批量插入慎用
InsertMany的ordered: false,它会让部分失败时继续执行,但错误位置和返回结果难对齐
事务与集合设计要注意什么
Kratos 项目常用于中后台或微服务,MongoDB 事务仅在副本集或分片集群下可用,单节点不支持。如果你的部署环境是单机开发版(比如 Docker 启动的 standalone),session.StartTransaction() 会直接 panic。
- 生产环境务必确认 MongoDB 是副本集(至少 3 节点),并在连接串中启用
replicaSet=myrs - 事务内所有操作必须在同一个
*mongo.Database下,跨库事务不被支持 - 集合名别用动态拼接(如
"user_" + tenantID),这会让索引、监控、备份变得不可维护;优先用字段区分租户,再配合tenant_id索引 -
_id字段尽量用primitive.ObjectID,别用字符串——它自带时间戳,方便按创建时间范围查询,也避免重复生成逻辑
真正麻烦的从来不是连上 MongoDB,而是让它的动态 schema 在强类型 Go 代码里不掉链子:struct 定义要预留扩展字段(如 Extra bson.M),UnmarshalBSON 方法得自己写,错误码得映射成 Kratos 的 ecode。这些细节没包住,上线后第一个空指针或字段丢失就出在 data 层。


















