Golang Kratos 系列:业务分层的若干思考(一)

在使用 Kratos 框架开发云服务的过程中,渐渐理解和感受到“领域层”这个概念和抽象的强大之处,它可以将业务和存储细节解耦、将业务和开发初期频繁变更的API结构,让Mock单元测试变得更加容易、对细节的变化更鲁棒。让业务代码摆脱技术细节依赖,使系统变更成本与业务复杂度解耦,领域层将领域层的放置位置应当遵循 “靠近使用者” 原则。以下是常见的分层方案和决策依据:


一、推荐架构(领域层独立)

.
├── internal
│   ├── domain   # 核心领域层(独立包)
│   │   ├── user.go        # 聚合根/值对象定义
│   │   ├── repository.go  # 仓储接口
│   │   └── service.go     # 领域服务  
│   │
│   ├── service  # 应用服务层
│   │   └── user.go        # 依赖domain包
│   │
│   └── data     # 数据层
│       └── user_repo.go   # 实现domain.Repository

二、分层决策依据

方案优点缺点适用场景
领域层独立1. 双向解耦
2. 明确分层界限
1. 多一个包目录
2. 需严格依赖管理
中大型项目
复杂业务逻辑
领域层在service1. 减少包数量
2. service直接使用
1. data层需反向依赖service
2. 易产生循环引用
小型项目
快速原型开发
领域层在data1. 数据层自包含1. service层被迫依赖data
2. 破坏分层架构
不推荐

三、具体实施示例

1. 独立领域层(推荐)
// internal/domain/user.go
package domaintype User struct {ID   UserIDName string
}type UserRepository interface {FindByID(context.Context, UserID) (*User, error)
}// internal/service/user.go
package servicetype UserService struct {repo domain.UserRepository // 依赖抽象
}// internal/data/user_repo.go
package datatype userRepo struct {db *gorm.DB
}func (r *userRepo) FindByID(ctx context.Context, id domain.UserID) (*domain.User, error) {// 实现domain.Repository
}
2. 领域层在service(备选)
// internal/service/domain/user.go
package servicetype User struct {ID   UserIDName string
}type UserRepository interface {FindByID(context.Context, UserID) (*User, error)
}// internal/data/user_repo.go
package dataimport "project/internal/service"type userRepo struct {db *gorm.DB
}func (r *userRepo) FindByID(ctx context.Context, id service.UserID) (*service.User, error) {// 需要依赖service包
}

四、关键设计原则

  1. 依赖方向不可逆

    高层模块(service) → 领域抽象(domain) ← 实现细节(data)
    
    • 永远保持 data 依赖 domain,而不是相反
    • 禁止 domain 导入 dataservice 的任何代码
  2. 接口所有权

    • 领域接口(如 Repository)由 领域层定义
    • data 层仅负责实现这些接口
  3. 类型系统控制

    // 正确定义(领域层拥有类型)
    type UserID string// 错误定义(data层定义类型)
    type User struct {gorm.Model // 基础设施细节泄露
    }
    
  4. 依赖注入管理

    // wire.go 中明确依赖关系
    func newService(repo domain.UserRepository) *service.UserService {return &service.UserService{repo: repo}
    }
    

五、Kratos 特化建议

1. 项目结构优化
.
├── api              # proto定义
├── internal
│   ├── biz          # 领域层(原domain)
│   │   ├── user.go
│   │   └── repo.go  # 仓储接口
│   ├── service      # 应用服务
│   └── data         # 数据实现
└── pkg             # 通用领域组件
2. 接口定义示例
// internal/biz/repo.go
package biztype UserRepo interface {Save(context.Context, *User) errorFindByID(context.Context, int64) (*User, error)
}// internal/data/user.go
package datatype userRepo struct {data *Data
}func (r *userRepo) FindByID(ctx context.Context, id int64) (*biz.User, error) {// 转换data -> biz
}
3. Wire 注入配置
// internal/data/data.go
func NewData() (*Data, func(), error) {// 初始化数据库等
}// internal/data/user.go
func NewUserRepo(data *Data) biz.UserRepo {return &userRepo{data: data}
}// internal/service/service.go
func NewService(repo biz.UserRepo) *Service {return &Service{repo: repo}
}

六、何时打破原则

在以下场景可考虑将领域模型放在 service 层:

  1. 原型验证阶段:快速迭代时减少目录切换
  2. 简单CRUD服务:无复杂业务规则
  3. 独立微服务:服务边界即领域边界

但需保证:

  • data 层不包含业务逻辑
  • 随时可以重构为独立领域层

七、架构验证检查表

  1. 编译时验证

    # 确保data不依赖service
    grep -r "import.*service" internal/data/
    
  2. 循环依赖检测

    go mod graph | awk '{print $1}' | sort | uniq -c | sort -nr
    
  3. 接口所有权检查

    # 确认Repository接口定义在domain/biz
    ls internal/domain/repo.go  
    
  4. 类型定义位置

    # 核心类型不应包含gorm标签
    grep -r "gorm:" internal/domain/
    

通过这种分层,您将获得:

  • 明确架构边界:各层职责清晰
  • 测试友好:可轻松Mock仓储实现
  • 演进自由:数据源切换不影响业务逻辑
  • 团队协作:领域专家可独立设计模型
  1. 附录
    Kratos 官方架构中与传统DDD概念的对应
Kratos 结构传统DDD概念核心职责
internal/biz领域层 (Domain)定义聚合根/实体/值对象/领域服务
internal/data基础设施层 (Infra)实现仓储接口、外部API适配
internal/service应用层 (Application)协调领域对象、处理业务流程

在下篇文章我们讨论在同时对容变API和数据层同时应如领域层隔离的案例。Golang Kratos 系列:业务分层的若干思考(二)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/911662.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/911662.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度优化OSS上传性能:多线程分片上传 vs 断点续传实战对比

1 卸载开头 对象存储服务(OSS)已成为现代应用架构的核心组件,但随着业务规模扩大,文件上传性能问题日益凸显。本文将深入探讨两种核心优化技术:多线程分片上传和断点续传,通过理论分析、代码实现和性能测试…

doris_工作使用整理

文章目录 前言一、doris整体情况二、doris的存储过程情况1.分类2. 同步物化视图3. 异步物化视图三,分区相关1.分区建的过多前言 提示:doris使用版本3.x 提示:以下是本篇文章正文内容,下面案例可供参考 一、doris整体情况 细节放大 二、doris的存储过程情况 1.分类 按…

左神算法之单辅助栈排序算法

目录 1. 题目2. 解释3. 思路4. 代码5. 总结 1. 题目 请编写一个程序,对一个栈里的整型数据,按升序进行排序(即排序前栈里的数据是无序的,排序后最大元素位于栈顶)。要求最多只能使用一个额外的栈存放临时数据&#xf…

使用Trae编辑器与MCP协议构建高德地图定制化服务

目录 一、使用Trae编辑器配置高德MCP Server 1.1 Trae介绍 1.2 从mcp.so中获取配置高德地图mcp server配置信息 1.3 高德地图开发者配置 1.4 添加Filesystem 到Trae 1.5 使用结果展示 1.6 MCP常见命令行工具和包管理说明 1.7 Function Call工具和MCP技术对比 二、本地…

【LLaMA-Factory 实战系列】三、命令行篇 - YAML 配置与高效微调 Qwen2.5-VL

【LLaMA-Factory 实战系列】三、命令行篇 - YAML 配置与高效微调 Qwen2.5-VL 1. 引言2. 为什么从 WebUI 转向命令行?3. 准备工作(回顾)4. 核心:创建并理解训练配置文件4.1 选择并复制基础模板4.2 逐一解析与修改配置文件4.3 参数详…

推荐:ToB销售B2B销售大客户营销大客户销售培训师培训讲师唐兴通讲销售技巧数字化销售销AI销售如何有效获取客户与业绩

站在AI浪潮之巅,重塑销售之魂 在AI时代,普通销售人员(TOB、TOC)除了传统的销售动作之外,还能做什么?怎么做? 这是《AI销冠》这本书想探讨的核心问题。 特别喜欢编辑老师总结的: 读者…

爬取小红书相关数据导入到excel

本期我们来进行实战,爬取小红书的相关数据导入到excel中,后续可进行些数据分析,今后或者已经在运营小红书的小伙伴应该比较喜欢这些数据。今天我们的主角是DrissionPage,相对于之前介绍的selenium省去了很多的配置,直接安装了就能使用。 DrissionPage 是一个基于 python …

c++面试题每日一学记录- C++对象模型与内存对齐深度原理详解

一、C++对象模型核心原理 1. 对象内存布局基础原理 设计哲学: 零开销原则:不为未使用的特性付出代价(如无虚函数则无vptr)兼容性:C结构体在C++中保持相同内存布局多态支持:通过虚函数表实现运行时动态绑定内存布局实现机制: 编译器处理步骤: 成员排列:严格按声明顺序…

Kafka 监控与调优实战指南(二)

五、Kafka 性能问题剖析 5.1 消息丢失 消息丢失是 Kafka 使用过程中较为严重的问题,可能由多种原因导致。在生产者端,如果配置不当,比如将acks参数设置为0,生产者发送消息后不会等待 Kafka broker 的确认,就继续发送…

Linux下SVN报错:Unable to connect to a repository at URL ‘svn://XXX‘

一、问题描述 Linux下通过SVN执行提交(commit)操作时报错:Unable to connect to a repository at URL svn://XXX: 二、解决方法 导致该问题的一个可能原因是远程仓库的URL发生变化了,即svn服务器的ip变更了。这时可…

Modbus 扫描 从站号、波特率

下载链接:https://pan.quark.cn/s/533ceb8e397d 下载链接: https://pan.baidu.com/s/1PQHn-MwfzrWgF2UrXQDoGg 提取码: 1111

Docker 容器通信与数据持久化

目录 简介 一、Docker 容器通信 1. Docker 网络模式 2. Bridge 模式 3. Host 模式 4. Container 模式 5. Overlay 模式 6. 端口映射:容器与外部的桥梁 7. 容器互联:从 --link 到自定义网络 二、Docker 数据持久化 1. 数据卷:Docke…

【教学类-89-08】20250624新年篇05——元宵节灯笼2CM黏贴边(倒置和正立数字 )

背景需求: 【教学类-89-06】20250220新年篇05——元宵节灯笼2CM黏贴边(3边形到50边形,一页1图、2图、4图,适合不同水平,适合不同阶段)-CSDN博客文章浏览阅读1.6k次,点赞35次,收藏27…

【DB2】SQL0104N An unexpected token “OCTETS“ was found following “……

db2创建表时报标题的错误,建表语句如下 db2 "CREATE TABLE YS.TEST_1(ID VARCHAR(64 OCTETS))"去掉octets就好了 经过测试,在9.7版本报错,在10.5.11没问题,怀疑版本差异导致 在官网查找资料,应该是10.5才…

暴雨以信创委员会成员单位身份参与南京专题活动

6月19日,中国电子工业标准化技术协会信息技术应用创新工作委员会(简称信创工委会)联合南京市工业和信息化局共同举办的“智启未来:AI赋能信息技术应用创新办公新势力”专题活动在南京成功举办。南京市工业和信息化局副局长代吉上、…

基于keepalived、vip实现高可用nginx (centos)

基于keepalived、vip实现高可用nginx (centos) 1、安装keepalived yum install keepalived2、选同一局域网空置ip作vip 我这里测试是: 主:192.168.163.134 副:192.168.163.135 vip:192.168.163.1403、ke…

使用 launch 启动 rviz2 并加载机器人模型

视频资料:《ROS 2机器人开发从入门到实践》6.2.2 在RViz中显示机器人_哔哩哔哩_bilibili 1、创建工作空间 chapt6_ws/src,创建包 fishrobot_description ros2 create fishrobot_description --build-type ament_cmake --license Apache-2.0 2、创建机器…

华为云Flexus+DeepSeek征文 | 基于CCE容器的AI Agent高可用部署架构与弹性扩容实践

华为云FlexusDeepSeek征文 | 基于CCE容器的AI Agent高可用部署架构与弹性扩容实践 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 …

Python学习Day41

学习来源:浙大疏锦行 知识回顾 数据增强卷积神经网络定义的写法batch归一化:调整一个批次的分布,常用与图像数据特征图:只有卷积操作输出的才叫特征图调度器:直接修改基础学习率 卷积操作常见流程如下: …

数组题解——最长回文子串【LeetCode】

5. 最长回文子串 一、向右拓展 算法思路 你用res记录当前找到的最长回文子串。每次遍历到s[i]时,尝试找到以s[i]结尾的、比当前res更长的回文子串。 先尝试长度为len(res)2(即起点i-len(res)-1)的子串,看是不是回文。如果不是&…