3 回答

TA貢獻1883條經驗 獲得超3個贊
存儲庫是保存數據的地方的表示,架構元素也是如此。
事務是解決非功能需求(原子操作)的技術細節,因此它必須像架構元素中的內部引用或私有功能一樣使用。
在這種情況下,如果您的存儲庫是這樣寫的:
type UserRepository interface {
func Keep(UserData) error
func Find(UUID) UserData
}
type ImageRepository interface {
func Keep(ImageData) error
func Find(UUID) ImageData
}
事務性方法是一個實現細節,因此您可以創建一個像內部引用一樣使用的 UserRepository 和 ImageRepository 的“實現”。
type UserRepositoryImpl struct {
Tx Transaction
}
func (r UserRepository) func Keep(UserData) error { return r.Tx.On(...)}
func (r UserRepository) func Find(UUID) UserData { return r.Tx.WithResult(...)}
通過這種方式,您也可以將用戶和圖像保留在單個事務中。
例如,如果客戶端引用了 userRepository 和 imageRepository 并且它負責 userData 和 imageData 并且它還希望將這兩個數據保存在單個事務中,那么:
//open transaction and set in participants
tx := openTransaction()
ur := NewUserRepository(tx)
ir := NewImageRepository(tx)
//keep user and image datas
err0 := ur.Keep(userData)
err1 := ir.Keep(imageData)
//decision
if err0 != nil || err1 != nil {
tx.Rollback()
return
}
tx.Commit()
這是干凈、客觀的,并且在洋蔥架構、DDD 和 3 層架構(Martin Fowler)中運行良好!
在洋蔥架構中:
實體:用戶和圖像(沒有業務規則)
Usecase : 存儲庫接口(應用規則:保留用戶和圖像)
控制器: A/N
DB/Api:客戶端、tx、存儲庫實現

TA貢獻1779條經驗 獲得超6個贊
保持存儲庫原樣,不要嘗試在那里解決事務性 API 的想法。您需要一個單獨的存儲庫注冊表來控制您的存儲庫將如何初始化以及它們的行為方式;原子操作等這是一個很好的例子:
文件:內部/存儲庫/registry.go
package repository
import (
"context"
"github.com/kataras/app/image"
)
type TransactionFunc = func(Registry) error
type Registry interface {
NewImageRepository() image.Repository
// more repo initialization funcs...
InTransaction(context.Context, func(Registry) error) error
}
文件:內部/存儲庫/注冊表/postgres.go
package registry
import (
"context"
"fmt"
"github.com/kataras/app/image"
"github.com/kataras/app/internal/repository"
"github.com/kataras/pg" // your or 3rd-party database driver package.
)
type PostgresRepositoryRegistry struct {
db *pg.DB
}
var _ repository.Registry = (*PostgresRepositoryRegistry)(nil)
func NewPostgresRepositoryRegistry(db *pg.DB) *PostgresRepositoryRegistry {
return &PostgresRepositoryRegistry{
db: db,
}
}
func (r *PostgresRepositoryRegistry) NewImageRepository() image.Repository {
return image.NewPostgresRepository(r.db)
}
// The important stuff!
func (r *PostgresRepositoryRegistry) InTransaction(ctx context.Context, fn repository.TransactionFunc) (err error) {
if r.db.IsTransaction() {
return fn(r)
}
var tx *pg.DB
tx, err = r.db.BeginDatabase(ctx)
if err != nil {
return
}
defer func() {
if p := recover(); p != nil {
_ = tx.RollbackDatabase(ctx)
panic(p)
} else if err != nil {
rollbackErr := tx.RollbackDatabase(ctx)
if rollbackErr != nil {
err = fmt.Errorf("%w: %s", err, rollbackErr.Error())
}
} else {
err = tx.CommitDatabase(ctx)
}
}()
newRegistry := NewPostgresRepositoryRegistry(tx)
err = fn(newRegistry)
return
}
現在,在您的域服務級別,您只需注入一個repository.Registry,例如PostgresRepositoryRegistry.
文件:內部/服務/image_service.go
package service
import (
"context"
"github.com/kataras/app/internal/repository"
)
type ImageService struct {
registry repository.Registry
}
func NewImageService (registry repository.Registry) *ImageService {
return &ImageService {
registry: registry ,
}
}
func (s *ImageService) DoSomeWork(ctx context.Context, ...) error {
images := s.registry.NewImageRepository()
images.DoSomeWork(ctx, ...)
}
// Important stuff!
func (s *ImageService) DoSomeWorkInTx(ctx context.Context, inputs [T]) error {
return s.registry.InTransaction(ctx, func(r repository.Registry) error) {
images := r.NewImageRepository()
for _, in := range inputs {
if err := images.DoSomeWork(); err!=nil {
return err // rollback.
}
}
return nil
}
}
在您的 API 路由中使用 ImageService。
db, err := pg.Open(...)
// handleError(err)
repoRegistry := registry.NewPostgresRepositoryRegistry(db)
imageService := service.NewImageService(repoRegistry)
// controller := &MyImageController{Service: imageService}
您可以使用Iris進行依賴注入。

TA貢獻1876條經驗 獲得超7個贊
如果你回購必須保留一些狀態字段
type UserRepositoryImpl struct {
db Transaction
someState bool
}
func (repo *UserRepositoryImpl) WithTx(tx Transaction) *UserRepositoryImpl {
newRepo := *repo
repo.db = tx
return &newRepo
}
func main() {
repo := &UserRepositoryImpl{
db: connectionInit(),
state: true,
}
repo.DoSomething()
tx := openTransaction()
txrepo := repo.WithTx(tx)
txrepo.DoSomething()
txrepo.DoSomethingElse()
}
- 3 回答
- 0 關注
- 146 瀏覽
添加回答
舉報