Skip to content

Commit

Permalink
Updated readme
Browse files Browse the repository at this point in the history
  • Loading branch information
aneshas committed Mar 15, 2024
1 parent e5c7b96 commit 5697c10
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 1 deletion.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ func (s *AccountService) ProvisionAccount(ctx context.Context, r ProvisionAccoun
You will notice that the service looks mostly the same as it would normally apart from embedding `Transactor` interface
and wrapping the use case execution using `WithTransaction`, both of which say nothing of the way the mechanism is implemented (no infrastructure dependencies).

If the function wrapped via `WithTransaction` errors out or panics the transaction itself will be rolled back and if nil error is
returned the transaction will be committed. (this behavior can be changed by providing `WithIgnoredErrors(...)` option to `tx.New`)

### Repo implementation
Then, your repo might use postgres with pgx and have the following example implementation:

Expand Down Expand Up @@ -133,4 +136,7 @@ func main() {

This way, your infrastructural concerns stay in the infrastructure layer where they really belong.

*Please note that this is only one way of using the abstraction*
*Please note that this is only one way of using the abstraction*

## Next up
- [ ] Add a way to configure transaction isolation levels for individual drivers eg. `pgxtx.NewDBFromPool(pool, ...opts)`
98 changes: 98 additions & 0 deletions example/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package example

import (
"context"
"github.com/aneshas/tx/v2"
"github.com/aneshas/tx/v2/pgxtxv5"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgxpool"
)

func main() {
var pool *pgxpool.Pool

svc := NewAccountService(
tx.New(pgxtxv5.NewDBFromPool(pool)),
NewAccountRepo(pool),
)

_ = svc
}

type Account struct {
// ...
}

type Repo interface {
Save(ctx context.Context, account Account) error
Find(ctx context.Context, id int) (*Account, error)
}

func NewAccountService(transactor tx.Transactor, repo Repo) *AccountService {
return &AccountService{Transactor: transactor, repo: repo}
}

type AccountService struct {
// Embedding transactional behavior in your service
tx.Transactor

repo Repo
}

type ProvisionAccountReq struct {
// ...
}

func (s *AccountService) ProvisionAccount(ctx context.Context, r ProvisionAccountReq) error {
return s.WithTransaction(ctx, func(ctx context.Context) error {
// ctx contains an embedded transaction and as long as
// we pass it to our repo methods, they will be able to unwrap it and use it

// eg. multiple calls to different repos

return s.repo.Save(ctx, Account{
// ...
})
})
}

func NewAccountRepo(pool *pgxpool.Pool) *AccountRepo {
return &AccountRepo{
pool: pool,
}
}

type AccountRepo struct {
pool *pgxpool.Pool
}

func (r *AccountRepo) Save(ctx context.Context, account Account) error {
_, err := r.conn(ctx).Exec(ctx, "...")

return err
}

func (r *AccountRepo) Find(ctx context.Context, id int) (*Account, error) {
rows, err := r.conn(ctx).Query(ctx, "...")
if err != nil {
return nil, err
}

_ = rows

return nil, nil
}

type Conn interface {
Exec(ctx context.Context, sql string, arguments ...any) (commandTag pgconn.CommandTag, err error)
Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
}

func (r *AccountRepo) conn(ctx context.Context) Conn {
if tx, ok := pgxtxv5.From(ctx); ok {
return tx
}

return r.pool
}

0 comments on commit 5697c10

Please sign in to comment.