I would question the framework design: the method is called "UpdateUser", so it should be executed in a transaction, so it should be a parameter of the service, and the transaction logic handled by the framework.

  func (s \*Service) UpdateUser(ctx context.Context, tx models.Repo, userID string) error {
        user, err := tx.GetUser(ctx, userID)
        if err != nil {
            return err
        }
        user.Name = "Updated"
        return tx.SaveUser(ctx, user)
  }
TN1ck10 hours ago | | | parent | | on: 47764129
In that instance, you are right, but there are often cases where you need to do multiple queries / updates spanning multiple tables in a single transaction, then you do need a generic transaction wrapper.