[Go言語]deferでトランザクション処理のエラーを返却する

## やりたいこと
 - defer関数内のerrorの値を、最終的なerrorの返り値として呼び出し元に返却したい
 - 一緒にCommit()とRollback()も上手く捌きたい ≒ 共通化したい

## 実装例
個人で作成してみた[参考レポジトリ](https://github.com/MatsuoTakuro/fcoin-balances-manager)から、一部抜粋しています。
ファイルを直接参照されたい場合は、[こちら](https://github.com/MatsuoTakuro/fcoin-balances-manager/blob/main/repository/tx.go)からどうぞ。
（if文が続いて読みにくい部分は、各々でリファクタリングしてご参考になればと思います :bow: ）

```tx.go
// トランザクションの共通処理をメソッド化
func (r *Repository) BeginTx(ctx context.Context, db Beginner) (*sql.Tx, func(context.Context, *sql.Tx, error) error, error) {
	tx, err := db.BeginTx(ctx, nil)
	if err != nil {
		return nil, nil, apperror.PROCESS_TRANSACTION_FAILED.Wrap(err, err.Error())
	}
    // commitOrRollback関数を返却する
	return tx, commitOrRollback, nil
}

// コミットの実行・コミット失敗時にロールバックする共通処理を関数化
func commitOrRollback(ctx context.Context, tx *sql.Tx, err error) error {
    // トランザクション処理が成功している場合
	if err == nil {
        // コミットを実行
		if err := tx.Commit(); err != nil {
            // コミット失敗の場合、ロールバックを実施
			if rbErr := tx.Rollback(); rbErr != nil {
                // ロールバック失敗の場合、その失敗時のエラーを返却
				return apperror.PROCESS_TRANSACTION_FAILED.Wrap(err, rbErr.Error())
			}
			err = apperror.PROCESS_TRANSACTION_FAILED.Wrap(err, err.Error())
			log.Printf("[%d]trans: rollbacked sucessfully for handling err: %v",
				appcontext.GetTracdID(ctx), err)
            // ロールバック成功の場合、コミット失敗時のエラーを返却
			return err
		}
        // コミット成功の場合、nilを返却
		return nil
    // トランザクション処理が失敗している場合
	} else {
        // ロールバックを実施
		if rbErr := tx.Rollback(); rbErr != nil {
            // ロールバック失敗の場合、その失敗時のエラーを返却
			return apperror.PROCESS_TRANSACTION_FAILED.Wrap(err, rbErr.Error())
		}
		log.Printf("[%d]trans: rollbacked sucessfully for handling err: %v",
			appcontext.GetTracdID(ctx), err)
        // ロールバック成功の場合、トランザクション処理時のエラーを返却
		return err
	}
}

// トランザクション処理の一例
// 新規ユーザの登録と残高作成を行うトランザクション処理
func (r *Repository) RegisterUserTx(
	ctx context.Context, db Beginner, name string,
// 返り値のerrorをerr変数で定義しておく
) (user *entity.User, balance *entity.Balance, err error) {

    // トランザクションの共通処理を呼び出し、commitOrRollback関数を受け取る
	tx, commitOrRollback, err := r.BeginTx(ctx, db)
	defer func() {
        // defer関数内でcommitOrRollback関数を呼び出す
        // errとは別に、txErr変数を定義・初期化する
        // txErrがnilである場合、errの値は上書かれず、そのまま本メソッド（RegisterUserTx）の呼び出し元に返却される
		if txErr := commitOrRollback(ctx, tx, err); txErr != nil {
            // txErrがnilでない場合、返り値であるerrにtxErrを代入（上書き）する
			err = txErr
            // 上書かれたこのerrの値が、本メソッド（RegisterUserTx）の呼び出し元に返却される
		}
	}()

	user, err = r.CreateUser(ctx, tx, name)
	if err != nil {
        // defer関数へ（errを上書く可能性あり）
		return nil, nil, err
	}

	balance, err = r.CreateBalance(ctx, tx, user.ID)
	if err != nil {
        // defer関数へ（errを上書く可能性あり）
		return nil, nil, err
	}
    // defer関数へ（nilを上書く可能性あり）
	return user, balance, nil
}
```
## 参考リポジトリ
https://github.com/MatsuoTakuro/fcoin-balances-manager

## 参考記事
 - 「defer関数内のerrorの値を、最終的なerrorの返り値として呼び出し元に返却したい」について参考になった記事 :bow: 

https://trstringer.com/golang-deferred-function-error-handling/

 - 「一緒にCommit()とRollback()も上手く捌きたい ≒ 共通化したい」について参考になった記事 :bow: 

https://qiita.com/fukuyama012/items/c0e0d1be991be56e0910

https://zenn.dev/mitsugu/articles/28f40a4f6b2b5c


## 追記
 - `commitOrRollback`関数のリファクタ版を追記
```go
// commitOrRollback handles committing a transaction or rolling it back in case of an error.
func commitOrRollback(ctx context.Context, tx *sql.Tx, err error) error {
	if err != nil {
		return rollbackTransaction(ctx, tx, err)
	}
	return commitTransaction(ctx, tx)
}

// rollbackTransaction handles the rollback of a transaction and logs the appropriate message.
func rollbackTransaction(ctx context.Context, tx *sql.Tx, err error) error {
	if rbErr := tx.Rollback(); rbErr != nil {
		return apperror.PROCESS_TRANSACTION_FAILED.Wrap(err, rbErr.Error())
	}
	log.Printf("[%d]trans: rollbacked sucessfully for handling err: %v",
		appcontext.GetTracdID(ctx), err)
	return err
}

// commitTransaction handles the commit of a transaction and logs the appropriate message.
func commitTransaction(ctx context.Context, tx *sql.Tx) error {
	if err := tx.Commit(); err != nil {
		return rollbackTransaction(ctx, tx, err)
	}
	return nil
}
```