How to Implement the Unit of Work Pattern in Go

Implement the Unit of Work pattern in Go by creating an interface to track changes and a struct to manage a single database transaction for atomic commits.

Implement the Unit of Work pattern in Go by defining an interface for tracking changes and a concrete struct that manages a transactional context.

package repository

type UnitOfWork interface {
    Commit() error
    Rollback() error
    Save(entity Entity) error
}

type unitOfWork struct {
    db *sql.DB
    tx *sql.Tx
    changes []Entity
}

func NewUnitOfWork(db *sql.DB) UnitOfWork {
    return &unitOfWork{db: db}
}

func (u *unitOfWork) Save(entity Entity) error {
    u.changes = append(u.changes, entity)
    return nil
}

func (u *unitOfWork) Commit() error {
    if u.tx == nil {
        var err error
        u.tx, err = u.db.Begin()
        if err != nil { return err }
    }
    for _, e := range u.changes {
        if err := e.Save(u.tx); err != nil { return err }
    }
    return u.tx.Commit()
}

func (u *unitOfWork) Rollback() error {
    if u.tx != nil { return u.tx.Rollback() }
    return nil
}