DDD::repo.go

repo.go 里面写的都是接口

ChatGPT said:

💯 完全正确!你抓到了 DDD 和 Kratos 架构的一个核心原则。

是的------repo.go(或 repository.go) 文件里写的就是接口(interface) ,不写具体实现。

下面我来系统讲清楚:为什么要这么做、repo 接口应该长什么样、它和 data 层是怎样配合的。


🧩 一、repo.go 是干嘛的?

在 DDD(领域驱动设计)中,仓储(Repository)是领域层的一部分,用来:

把领域对象(Entity / Aggregate)从持久化介质中取出或保存进去,

并屏蔽数据库等基础设施的细节。

也就是说:

  • 领域层(domain) 只定义仓储接口(描述"要干什么");
  • 数据层(data) 来实现这些接口(描述"怎么干")。

📦 二、典型的 repo.go 示例

假设我们在 internal/domain/order/ 下有一个 repo.go

scss 复制代码
package order

import "context"

// Repository 是订单仓储接口
type Repository interface {
	// 保存订单
	Save(ctx context.Context, order *Order) error

	// 根据ID获取订单
	FindByID(ctx context.Context, id OrderID) (*Order, error)

	// 获取用户的所有订单
	ListByUserID(ctx context.Context, userID string) ([]*Order, error)

	// 删除订单
	Delete(ctx context.Context, id OrderID) error
}

特点:

  • 只定义接口(不关心怎么实现);
  • ✅ 入参/出参都是领域对象(Order, OrderID);
  • ✅ 不暴露数据库类型(如 gorm.Modelsql.DB);
  • ✅ 语义贴近领域(Save、FindByID、ListByUserID)。

🏗️ 三、为什么接口放在领域层?

👉 这是 依赖倒置原则(Dependency Inversion Principle) 的体现:

职责 依赖方向
usecase 调用仓储做业务逻辑 依赖 domain.Repository 接口
domain 定义领域模型 + 仓储接口 不依赖下层
data 实现仓储接口 依赖 domain.Repository 定义

也就是说:

kotlin 复制代码
usecase ───→ domain (interface)
             ↑
             └── data (implementation)

这样:

  • usecase 只知道接口,不知道数据库;
  • data 层只管实现接口,替换实现不会影响上层逻辑;
  • 业务层和数据层完全解耦。

⚙️ 四、对应的 data 层实现

internal/data/order_repo.go

go 复制代码
package data

import (
	"context"
	"ddd-kratos/internal/domain/order"
)

type orderRepo struct {
	data *Data // Data 持有数据库、缓存等实例
}

// NewOrderRepo 是仓储的构造函数
func NewOrderRepo(data *Data) order.Repository {
	return &orderRepo{data: data}
}

func (r *orderRepo) Save(ctx context.Context, o *order.Order) error {
	return r.data.db.WithContext(ctx).Save(o).Error
}

func (r *orderRepo) FindByID(ctx context.Context, id order.OrderID) (*order.Order, error) {
	var entity order.Order
	if err := r.data.db.WithContext(ctx).First(&entity, "id = ?", id).Error; err != nil {
		return nil, err
	}
	return &entity, nil
}

func (r *orderRepo) ListByUserID(ctx context.Context, userID string) ([]*order.Order, error) {
	var list []*order.Order
	err := r.data.db.WithContext(ctx).Where("user_id = ?", userID).Find(&list).Error
	return list, err
}

func (r *orderRepo) Delete(ctx context.Context, id order.OrderID) error {
	return r.data.db.WithContext(ctx).Delete(&order.Order{}, "id = ?", id).Error
}

🧠 五、这样分层的好处

优点 说明
解耦 业务逻辑不依赖数据库实现
🧪 可测试 usecase 层可以用 mock repository 测试
🔄 可替换 想从 MySQL 换成 MongoDB,只需改 data 层
🧱 结构清晰 domain 纯业务,data 纯实现

✅ 总结一句话

repo.go 写的是 仓储接口 ,位于 domain 层;

它定义了"要提供什么数据访问能力",而不管"怎么实现";

具体的实现放在 data 层,用数据库或其他方式实现这些接口。

相关推荐
卓卓不是桌桌2 小时前
如何优雅地处理 iframe 跨域通信?这是我的开源方案
javascript·架构
Qlly2 小时前
DDD 架构为什么适合 MCP Server 开发?
人工智能·后端·架构
用户881586910911 天前
AI Agent 协作系统架构设计与实践
架构
鹏北海1 天前
Qiankun 微前端实战踩坑历程
前端·架构
货拉拉技术1 天前
货拉拉海豚平台-大模型推理加速工程化实践
人工智能·后端·架构
RoyLin1 天前
libkrun 深度解析:架构设计、模块实现与 Windows WHPX 后端
架构
CoovallyAIHub2 天前
实时视觉AI智能体框架来了!Vision Agents 狂揽7K Star,延迟低至30ms,YOLO+Gemini实时联动!
算法·架构·github
RoyLin2 天前
领域驱动设计:回归本质的工程实践
架构
CoovallyAIHub2 天前
OpenClaw:从“19万星标”到“行业封杀”,这只“赛博龙虾”究竟触动了谁的神经?
算法·架构·github
悟空聊架构2 天前
基于KaiwuDB在游乐场“刷卡+投币”双模消费系统中的落地实践
数据库·后端·架构