3 回答

TA貢獻(xiàn)1883條經(jīng)驗(yàn) 獲得超3個(gè)贊
存儲(chǔ)庫(kù)是保存數(shù)據(jù)的地方的表示,架構(gòu)元素也是如此。
事務(wù)是解決非功能需求(原子操作)的技術(shù)細(xì)節(jié),因此它必須像架構(gòu)元素中的內(nèi)部引用或私有功能一樣使用。
在這種情況下,如果您的存儲(chǔ)庫(kù)是這樣寫的:
type UserRepository interface {
func Keep(UserData) error
func Find(UUID) UserData
}
type ImageRepository interface {
func Keep(ImageData) error
func Find(UUID) ImageData
}
事務(wù)性方法是一個(gè)實(shí)現(xiàn)細(xì)節(jié),因此您可以創(chuàng)建一個(gè)像內(nèi)部引用一樣使用的 UserRepository 和 ImageRepository 的“實(shí)現(xiàn)”。
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(...)}
通過這種方式,您也可以將用戶和圖像保留在單個(gè)事務(wù)中。
例如,如果客戶端引用了 userRepository 和 imageRepository 并且它負(fù)責(zé) userData 和 imageData 并且它還希望將這兩個(gè)數(shù)據(jù)保存在單個(gè)事務(wù)中,那么:
//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()
這是干凈、客觀的,并且在洋蔥架構(gòu)、DDD 和 3 層架構(gòu)(Martin Fowler)中運(yùn)行良好!
在洋蔥架構(gòu)中:
實(shí)體:用戶和圖像(沒有業(yè)務(wù)規(guī)則)
Usecase : 存儲(chǔ)庫(kù)接口(應(yīng)用規(guī)則:保留用戶和圖像)
控制器: A/N
DB/Api:客戶端、tx、存儲(chǔ)庫(kù)實(shí)現(xiàn)

TA貢獻(xiàn)1779條經(jīng)驗(yàn) 獲得超6個(gè)贊
保持存儲(chǔ)庫(kù)原樣,不要嘗試在那里解決事務(wù)性 API 的想法。您需要一個(gè)單獨(dú)的存儲(chǔ)庫(kù)注冊(cè)表來控制您的存儲(chǔ)庫(kù)將如何初始化以及它們的行為方式;原子操作等這是一個(gè)很好的例子:
文件:內(nèi)部/存儲(chǔ)庫(kù)/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
}
文件:內(nèi)部/存儲(chǔ)庫(kù)/注冊(cè)表/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
}
現(xiàn)在,在您的域服務(wù)級(jí)別,您只需注入一個(gè)repository.Registry,例如PostgresRepositoryRegistry.
文件:內(nèi)部/服務(wù)/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進(jìn)行依賴注入。

TA貢獻(xiàn)1876條經(jīng)驗(yàn) 獲得超7個(gè)贊
如果你回購(gòu)必須保留一些狀態(tài)字段
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 關(guān)注
- 154 瀏覽
添加回答
舉報(bào)