著者が、Go言語でsqlcを使いUnit of Work(UoW、複数のDB書き込みを1つのアトミックな操作としてまとめるパターン)を実装した経験を紹介しています。Go 1.26、sqlc v1.31.1、PostgreSQL 17.6、pgx v5のdatabase/sql互換ドライバという組み合わせで検証されています。
ポイントは、sqlcが生成するDBTXインターフェースが*sql.DBと*sql.Txの両方を抽象化する点です。これによりリポジトリのメソッドはトランザクションの内外で同じコードのまま動作します。トランザクション状態はcontextに載せて管理し、そのキーは他パッケージと衝突しないよう非公開の構造体型にしているとのことです。
またDoInTxという関数により、ネストしたトランザクション呼び出しを検知し、新しい接続を張らず既存のトランザクションに「相乗り」させる仕組みも実装しています。ネストしたBeginTx呼び出しが別々のセッションを作ってしまうとアトミック性が壊れるためです。著者はMartin FowlerのPoEAA(『Patterns of Enterprise Application Architecture』)で定義された元々のUoWパターンにも触れ、オブジェクトの変更追跡と書き込み調整を含む本来の定義に対し、Go実装では通常トランザクション境界管理のみに簡略化されると説明しています。
出典が伝えている要点
- 記事はGo 1.26、sqlc v1.31.1、PostgreSQL 17.6の組み合わせを使用している
- データベースドライバとしてpgx v5のdatabase/sql互換ドライバを使用している
- sqlcが生成するDBTXインターフェースは*sql.DBと*sql.Txの両方を抽象化する
- DBTXインターフェースにより、リポジトリのメソッドはトランザクション内外で同じコードで動作する
- トランザクション状態をcontextに載せて管理する実装になっている
- contextに載せるキーは、他パッケージと衝突しないよう非公開の構造体型にしている
- DoInTxという関数が、ネストしたトランザクション呼び出しを検知する機能を持つ
- DoInTxは、ネスト呼び出しの場合に新しいトランザクション接続を張らず、既存のトランザクションに「相乗り」させる
- ネストしたBeginTx呼び出しが別々のセッションを作ってしまうと、アトミック性が壊れる問題があるとされている
- 著者はMartin FowlerのPatterns of Enterprise Application Architecture(PoEAA)で定義された元々のUnit of Workパターンに言及している
- PoEAAで定義された元々のパターンにはオブジェクトの変更追跡と書き込み調整の両方が含まれていたが、Go実装では通常トランザクション境界管理のみに簡略化されるとされている
原文より
ctx へ載せるキーは、他のパッケージと衝突しないよう非公開の構造体型にします
出典: zenn.dev