From 85e91699be520542e303ff549cab27ddc6d103e8 Mon Sep 17 00:00:00 2001 From: Kristian Borgwarth <10348902@pm.me> Date: Thu, 26 Mar 2026 07:49:41 +0100 Subject: [PATCH 1/2] feat(uow): add uow --- persistence/repositories/uow.go | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 persistence/repositories/uow.go diff --git a/persistence/repositories/uow.go b/persistence/repositories/uow.go new file mode 100644 index 0000000..3077ae2 --- /dev/null +++ b/persistence/repositories/uow.go @@ -0,0 +1,34 @@ +package repositories + +import ( + "context" + "database/sql" +) + +type UnitOfWork struct { + db *sql.DB +} + +func NewUnitOfWork(db *sql.DB) *UnitOfWork { + return &UnitOfWork{db: db} +} + +func (u *UnitOfWork) Execute(ctx context.Context, fn func(tx *sql.Tx) error) error { + tx, err := u.db.BeginTx(ctx, nil) + if err != nil { + return err + } + + defer func() { + if err != nil { + _ = tx.Rollback() + } + }() + + if err := fn(tx); err != nil { + tx.Rollback() + return err + } + + return tx.Commit() +} From 79066ee63858fbf8e480fa59f0b9987eeb86c9b8 Mon Sep 17 00:00:00 2001 From: Kristian Borgwarth <10348902@pm.me> Date: Thu, 26 Mar 2026 22:41:34 +0100 Subject: [PATCH 2/2] uow setup --- core/handlers/create_note.go | 4 ++- core/services/note_service.go | 0 persistence/repositories/note_repository.go | 17 ++++++----- persistence/repositories/tag_repository.go | 34 ++++++++------------- persistence/repositories/uow.go | 18 +++++++---- 5 files changed, 36 insertions(+), 37 deletions(-) create mode 100644 core/services/note_service.go diff --git a/core/handlers/create_note.go b/core/handlers/create_note.go index d1b462a..8e3361b 100644 --- a/core/handlers/create_note.go +++ b/core/handlers/create_note.go @@ -2,6 +2,7 @@ package handlers import ( "bytes" + "context" "encoding/json" "os" @@ -18,11 +19,12 @@ type createNoteCommand struct { } type CreateNoteHandler struct { + uow repositories.UnitOfWork noteRepo repositories.NoteRepository tagRepo repositories.TagRepository } -func (h CreateNoteHandler) Handle(params []byte) (*rpc.Response, *rpc.Error) { +func (h CreateNoteHandler) Handle(ctx context.Context, params []byte) (*rpc.Response, *rpc.Error) { var cmd createNoteCommand if err := json.Unmarshal(params, &cmd); err != nil { diff --git a/core/services/note_service.go b/core/services/note_service.go new file mode 100644 index 0000000..e69de29 diff --git a/persistence/repositories/note_repository.go b/persistence/repositories/note_repository.go index 6832f41..0c858b7 100644 --- a/persistence/repositories/note_repository.go +++ b/persistence/repositories/note_repository.go @@ -1,21 +1,22 @@ package repositories -import "database/sql" +import ( + "context" +) type NoteRepository interface { - Upsert(title string, path string, slug string) error + Upsert(ctx context.Context, title string, path string, slug string) error } type noteRepository struct { - db *sql.DB + dbCtx DBContext } -func NewNoteRepository(db *sql.DB) NoteRepository { - return ¬eRepository{db: db} +func NewNoteRepository(dbContext DBContext) NoteRepository { + return ¬eRepository{dbCtx: dbContext} } - -func (r *noteRepository) Upsert(title string, path string, slug string) error { +func (r *noteRepository) Upsert(ctx context.Context, title string, path string, slug string) error { query := ` INSERT INTO notes (title, path, slug) VALUES ($1, $2, $3) @@ -23,6 +24,6 @@ func (r *noteRepository) Upsert(title string, path string, slug string) error { SET title = EXCLUDED.title, path = EXCLUDED.path; ` - _, err := r.db.Exec(query, title, path, slug) + _, err := r.dbCtx.ExecContext(ctx, query, title, path, slug) return err } diff --git a/persistence/repositories/tag_repository.go b/persistence/repositories/tag_repository.go index 0906a5f..3f8b950 100644 --- a/persistence/repositories/tag_repository.go +++ b/persistence/repositories/tag_repository.go @@ -1,33 +1,27 @@ package repositories import ( - "database/sql" + "context" "strings" ) type TagRepository interface { - Upsert(names []string) error + Upsert(ctx context.Context, names []string) error } type tagRepository struct { - db *sql.DB + dbContext DBContext } -func NewTagRepository(db *sql.DB) TagRepository { - return &tagRepository{db: db} +func NewTagRepository(dbContext DBContext) TagRepository { + return &tagRepository{dbContext: dbContext} } -func (r *tagRepository) Upsert(names []string) error { +func (r *tagRepository) Upsert(ctx context.Context, names []string) error { if len(names) == 0 { return nil } - tx, err := r.db.Begin() - if err != nil { - return err - } - defer tx.Rollback() - placeholders := make([]string, 0, len(names)) args := make([]any, 0, len(names)) @@ -42,11 +36,12 @@ func (r *tagRepository) Upsert(names []string) error { "SELECT name FROM input " + "ON CONFLICT(name) DO NOTHING;" - if _, err := tx.Exec(query, args...); err != nil { + _, err := r.dbContext.ExecContext(ctx, query, args) + if err != nil { return err } - return tx.Commit() + return nil } func (r *tagRepository) UpsertNoteTags(noteID int64, tagIDs []int64) error { @@ -54,12 +49,6 @@ func (r *tagRepository) UpsertNoteTags(noteID int64, tagIDs []int64) error { return nil } - tx, err := r.db.Begin() - if err != nil { - return err - } - defer tx.Rollback() - placeholders := make([]string, 0, len(tagIDs)) args := make([]any, 0, len(tagIDs)) @@ -75,9 +64,10 @@ func (r *tagRepository) UpsertNoteTags(noteID int64, tagIDs []int64) error { "ON CONFLICT(note_id, tag_id) DO NOTHING" + "SELECT note_id, tag_id FROM input;" - if _, err := tx.Exec(query, args...); err != nil { + _, err := r.dbContext.ExecContext(context.Background(), query, args) + if err != nil { return err } - return tx.Commit() + return nil } diff --git a/persistence/repositories/uow.go b/persistence/repositories/uow.go index 3077ae2..f2802c8 100644 --- a/persistence/repositories/uow.go +++ b/persistence/repositories/uow.go @@ -5,6 +5,12 @@ import ( "database/sql" ) +type DBContext interface { + ExecContext(ctx context.Context, args ...any) (sql.Result, error) + QueryContext(ctx context.Context, args ...any) (*sql.Rows, error) + QueryRowContext(ctx context.Context, args ...any) *sql.Row +} + type UnitOfWork struct { db *sql.DB } @@ -13,22 +19,22 @@ func NewUnitOfWork(db *sql.DB) *UnitOfWork { return &UnitOfWork{db: db} } -func (u *UnitOfWork) Execute(ctx context.Context, fn func(tx *sql.Tx) error) error { - tx, err := u.db.BeginTx(ctx, nil) +func (u *UnitOfWork) Execute(ctx context.Context, fn func(tx *sql.Tx) error) (err error) { + transcation, err := u.db.BeginTx(ctx, nil) if err != nil { return err } defer func() { if err != nil { - _ = tx.Rollback() + _ = transcation.Rollback() } }() - if err := fn(tx); err != nil { - tx.Rollback() + if err = fn(transcation); err != nil { return err } - return tx.Commit() + err = transcation.Commit() + return err }