我正在试图找出一个很好的解决方案来管理Golang中的数据库事务,并在不同的服务之间使用相同的事务.
假设我正在构建一个论坛,这个论坛有posts
个和comments
个.
我在数据库中的posts
表上有comments_count
列,它跟踪帖子的 comments 数量.
当我为给定的帖子创建 comments 时,我还需要更新posts
表并增加该帖子的comments_count
列.
我的项目 struct 由几个层组成:数据库/业务/Web
目前我的代码是这样的吗?
main.go个
package main
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
"net/http"
"vkosev/stack/db/repository"
"vkosev/stack/services"
"vkosev/stack/web"
)
func main() {
dbConString := "postgres://user:password@host:port/database"
dbPool, _ := pgxpool.New(context.Background(), dbConString)
postRepo := repository.NewPostRepository(dbPool)
commentRepo := repository.NewCommentRepository(dbPool)
postService := services.NewPostService(postRepo)
commentService := services.NewCommentService(commentRepo)
handler := web.NewHandler(postService, commentService)
mux := http.NewServeMux()
mux.HandleFunc("POST /comments/{postId}", handler.CreateComment)
_ = http.ListenAndServe(":8080", mux)
}
web.go个
package web
type Handler struct {
postService *services.PostService
commentService *services.CommentService
}
func NewHandler(postService *services.PostService, commentService *services.CommentService) *Handler {
return &Handler{
postService: postService,
commentService: commentService,
}
}
func (h *Handler) CreateComment(w http.ResponseWriter, r *http.Request) {
postId := getPostIdeFromRequest(r)
comment := getCommentFromRequest(r)
newComment := h.commentService.Create(comment, postId)
err := h.postService.IncreaseCount(postId)
if err != nil {
// write some error message
}
writeJSON(w, http.StatusOK, newComment)
}
services.go个
package services
type PostService struct {
postRepo *repository.PostRepository
}
func NewPostService(postRepo *repository.PostRepository) *PostService {
return &PostService{postRepo: postRepo}
}
func (ps *PostService) IncreaseCount(postId int) error {
return ps.postRepo.IncreaseCount(postId)
}
type CommentService struct {
commentRepo *repository.CommentRepository
}
func NewCommentService(commentRepo *repository.CommentRepository) *CommentService {
return &CommentService{commentRepo: commentRepo}
}
func (cs *CommentService) Create(comment models.Comment, postId int) *models.Comment {
return cs.commentRepo.Save(comment, postId)
}
repository.go个
package repository
type PostRepository struct {
pool *pgxpool.Pool
}
func NewPostRepository(pool *pgxpool.Pool) *PostRepository {
return &PostRepository{pool: pool}
}
func (pr *PostRepository) IncreaseCount(postId int) error {
// call pr.pool and increase comments count for post with the given ID
}
type CommentRepository struct {
pool *pgxpool.Pool
}
func NewCommentRepository(pool *pgxpool.Pool) *CommentRepository {
return &CommentRepository{pool: pool}
}
func (cr *CommentRepository) Save(comment models.Comment, postId int) *models.Comment {
// call cr.pool and insert comment into the DB
}
我在main.go
中初始化所有必要的依赖项,并将它们注入到需要它们的位置,然后使用处理程序来处理每条路由.
现在,我需要一个事务,以便在由于某种原因无法更新帖子的 comments 计数时,回滚 comments 的创建.
我想最简单的方法是只将Tx
传入方法,但这看起来很难看.
我希望有某种方法来抽象数据库逻辑,这样存储库就不会关心它们是否使用了事务.
并且还管理这handler
种方法中的交易.
这样我就可以拥有这样的东西:
func (h *Handler) CreateComment(w http.ResponseWriter, r *http.Request) {
postId := getPostIdeFromRequest(r)
comment := getCommentFromRequest(r)
// Begin transaction
newComment := h.commentService.Create(comment, postId)
err := h.postService.IncreaseCount(postId)
if err != nil {
// rollback the transaction
// write some error message
}
// commit the transaction
writeJSON(w, http.StatusOK, newComment)
}