Closed9

Clean ArchitectureでDBのTransactionをどう表現するか?

youchanyouchan

なんらかのアプリケーションを書いていて悩むはずであろう普遍的なトピックなのかと思っていたが、スタンダードなソリューションはなかった。

ある程度世に出ているものを羅列していこうと思う。

youchanyouchan

筆者の見立てでは、変更の原子性に関してもusecase層(あるいはservice層、つまりソフトウェアの機能要件を満たす振る舞いを表現する層)で書くべきだと考えている。
これが表現されていないと、usecase層で表現されたデータ変更の原子性が担保されていなくても良いように認識されてしまう。Design Docなどのドキュメントで前提を補うことはできたとしても、可能な限り仕様に関しては見えるべきところで明確に表現するべきだ。

youchanyouchan

ここでいう原子性というのは「複数の処理を一つのまとまりとして扱うことで、その処理が完全に実行されるか、もしくは実行されないかのどちらかになることを保証する性質」を指す。

ACID特性のAの部分。
https://e-words.jp/w/ACID特性.html

youchanyouchan

Middlewareからcontext.Contextに注入するパターン

https://rinoguchi.net/2022/09/go-clean-architecture-transaction.html

  • 情報の作成・更新全てにトランザクションを張ることになる(そうしないこともできるが複雑になる)
    • パフォーマンスに懸念
  • 監査ログの収集する仕組みを構築する際に複雑化しそう
    • commitと同時に監査ログを溜めなければならない
  • 構造としてはシンプル
youchanyouchan

usecase層相当のレイヤーで表現するパターン

https://tech.mirrativ.stream/entry/2020/11/30/142354

  • トランザクションの抽象化をどこで行うかが悩みポイント
  • トランザクション相当の処理をrepository層に生やすパターンもある
youchanyouchan

repositoryのレイヤーで書くことにした。usecaseの層で表現できて、詳細はrepositoryに隠せる。

func (t *Transaction) Do(ctx context.Context, runner func(ctx context.Context) error) error {
        // ....
	err = runner(ctx)
	if err != nil {
                 // rollback
		return err
	}
	return nil
}
このスクラップは2023/03/17にクローズされました