gpt4 book ai didi

go - 存储库模式和连接表

转载 作者:行者123 更新时间:2023-12-01 22:05:38 25 4
gpt4 key购买 nike

我目前正在尝试围绕域驱动设计、实体、服务、存储库...构建我的应用程序

所有基本的 crud 操作都很简单,基本上是 1 个实体 => 1 个表 => 1 个存储库 => 1 个服务

但我想不出最干净的方法来处理两个实体之间的连接表。

可以通过连接内的表进行 1 次查询,这将是“干净的”(可以这么说),但效率不高,因为简单的连接会导致一次查询。

在这种模式下表在哪里连接?

  • 我一直在考虑构建现在封装答案的实体,但会有效地为 1 个查询创建 1 个实体 + 存储库...

  • 我也在考虑将多个实体合并到一个界面中可能会部分解决这个问题,但它会导致我的实体的许多空参数(执行时很少需要所有表中的所有字段加入)

解决这个问题的正确方法/模式是什么适合 DDD 或至少是干净的?

--编辑示例:

type User struct {
ID int `db:"id"`
ProjectID int `db:"project_id"`
RoleID int `db:"role_id"`
Email string `db:"email"`
FirstName string `db:"first_name"`
LastName string `db:"last_name"`
Password string `db:"password"`
}

type UserRepository interface {
FindById(int) (*User, error)
FindByEmail(string) (*User, error)
Create(user *User) error
Update(user *User) error
Delete(int) errorr
}

type Project struct {
ID int `db:"id"``
Name string `db:"name"`
Description string `db:"description"`
}

这里我有一个简单的用户存储库。我对“项目”表有类似的东西。可以创建表,获取项目的所有信息,删除等等。

如您所见,UserID 具有其所属项目 ID 的外键。

我的问题是当我需要从用户那里检索所有信息并说出“项目名称”和描述时。 (我实际上表/实体有更多参数)

我需要在 user.project_id 和 project.id 中做一个简单的连接,并在一个查询中检索用户 + 项目名称 + 描述的所有信息。

有时它会更复杂,因为会有 3-4 个实体像这样链接。 (用户、项目、项目附加信息、角色等)

当然我可以进行 N 个查询,每个实体一个。

user := userRepo.Find(user_id)
project := projectRepo.FindByuser(user.deal_id)

那会“有效”,但我正试图找到一种在一个查询中完成它的方法。因为 user.project_id 和 project.id 上的简单 sql 连接会在查询中提供所有数据。

最佳答案

至于 join 部分,您的问题很容易回答,但是对于 DDD,当前的语言可能性存在很多障碍。但我会试一试..

好吧,假设我们正在开发一个支持多语言的教育类(class)后端,我们需要连接两个表并随后映射到对象。我们有两个表(第一个包含与语言无关的数据,第二个包含与语言相关的数据)如果您是存储库倡导者,那么您将拥有类似的东西:

// Course represents e.g. calculus, combinatorics, etc.
type Course struct {
ID uint `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Poster string `json:"poster" db:"poster"`
}

type CourseRepository interface {
List(ctx context.Context, localeID uint) ([]Course, error)
}

然后为 sql db 实现它,我们将得到类似的东西:

type courseRepository struct {
db *sqlx.DB
}

func NewCourseRepository(db *sqlx.DB) (CourseRepository, error) {
if db == nil {
return nil, errors.New("provided db handle to course repository is nil")
}

return &courseRepository{db:db}, nil
}

func (r *courseRepository) List(ctx context.Context, localeID uint) ([]Course, error) {

const query = `SELECT c.id, c.poster, ct.name FROM courses AS c JOIN courses_t AS ct ON c.id = ct.id WHERE ct.locale = $1`
var courses []Course
if err := r.db.SelectContext(ctx, &courses, query, localeID); err != nil {
return nil, fmt.Errorf("courses repostory/problem while trying to retrieve courses from database: %w", err)
}

return courses, nil
}

这同样适用于不同的相关对象。您只需要耐心地为您的对象与基础数据的映射建模。再举个例子。

type City struct {
ID uint `db:"id"`
Country Country `db:"country"`
}

type Country struct {
ID uint `db:"id"`
Name string `db:"name"`
}

// CityRepository provides access to city store.
type CityRepository interface {
Get(ctx context.Context, cityID uint) (*City, error)
}

// Get retrieve city from database by specified id
func (r *cityRepository) Get(ctx context.Context, cityID uint) (*City, error) {

const query = `SELECT
city.id, country.id AS 'country.id', country.name AS 'country.name',
FROM city JOIN country ON city.country_id = country.id WHERE city.id = ?`

city := City{}
if err := r.db.GetContext(ctx, &city, query, cityID); err != nil {
if err == sql.ErrNoRows {
return nil, ErrNoCityEntity
}
return nil, fmt.Errorf("city repository / problem occurred while trying to retrieve city from database: %w", err)
}

return &city, nil
}

现在,一切看起来都很干净,直到您意识到 Go 实际上(就目前而言)不支持泛型,此外,在大多数情况下人们不鼓励使用反射功能,因为它会使您的程序变慢。为了彻底打乱你的想象,从这一刻起你需要交易功能......

如果你来自其他语言,你可以尝试用类似的东西来实现它:

// UnitOfWork is the interface that any UnitOfWork has to follow
// the only methods it as are to return Repositories that work
// together to achieve a common purpose/work.
type UnitOfWork interface {
Entities() EntityRepository
OtherEntities() OtherEntityRepository
}

// StartUnitOfWork it's the way to initialize a typed UoW, it has a uowFn
// which is the callback where all the work should be done, it also has the
// repositories, which are all the Repositories that belong to this UoW
type StartUnitOfWork func(ctx context.Context, t Type, uowFn UnitOfWorkFn, repositories ...interface{}) error

// UnitOfWorkFn is the signature of the function
// that is the callback of the StartUnitOfWork
type UnitOfWorkFn func(ctx context.Context, uw UnitOfWork) error

我故意错过了一个实现,因为它对于 sql 来说看起来很可怕并且值得它自己的问题(这个想法是工作单元有它的存储库版本,在引擎盖下用 started tx 装饰)并且在你解决这个问题之后你会有更多或更少

err = svc.startUnitOfWork(ctx, uow.Write, func(ctx context.Context, uw uow.UnitOfWork) error {

// _ = uw.Entities().Store(entity)
// _ = uw.OtherEntities().Store(otherEntity)

return nil
}, svc.entityRepository, svc.otherEntityRepository)

所以你到了决赛,在大多数情况下,人们开始说你写的代码似乎不符合习惯,指的是像 that 这样的东西。 .关键是概念写得太抽象了,物化 DDD 是否适用于 Golang 还是你可以部分模仿它是一个哲学问题。如果要灵 active ,选择数据库一次,使用纯db handle操作

关于go - 存储库模式和连接表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62032468/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com