ActiveRecord

トランザクションを貼る位置はどこがいいのか

トランザクションを貼る位置について考える機会があったので、それをまとめたいと思います。

トランザクションを貼る目的

トランザクションを貼る目的としては、データの整合性を担保したいからです。例えば次のようなコードを考えます。

def create
  subscription = Subscription.create(subscription_params)
  SubscriptionLogService.execute!(subscription) # ログを作成
end

サブスクリプションのレコードを作成した後、そのログレコードを作成しています。このとき、ログレコードの作成に失敗したときにどうなるかというと、サブスクリプションは作成されたが、そのログレコードは残っていないという状況になります。これではデータの整合性が担保されません。これを防ぐためにトランザクションを貼ります。

def create
  ActiveRecord::Base.transaction do
    subscription = Subscription.create(subscription_params)
    SubscriptionLogService.execute!(subscription)
  end
end

こうすることで、ログ作成の箇所で失敗したとしても、サブスクリプションレコードが作られる前までロールバックすることができます。

トランザクションを貼る位置

トランザクションを貼る位置はデータの整合性を担保したい最小単位で貼るのがいいです。例えば以下のようにコントローラーで貼るのがいいのか、ServiceやModelで貼るのがいいのかという議論が出てきます。下の例はコントローラーで貼っている例です。

def create
  ActiveRecord::Base.transaction do
    SubscriptionCreateService.execute!(subscription_params)
  end
end

# subscription_create_service.rb
def execute!(params)
  subscription = Subscription.create(params)
  SubscriptionLogService.execute(subscription)
end

この場合は、Serviceで貼ったほうがいいです。なぜなら、整合性を担保したい最小単位で貼ろうとすると、Serviceになるからです。

プログラムの意図としては、サブスクリプションのレコードとそのログレコードで合わせたいはずです。だとすると、整合性担保の最小単位はexecute!の中ということになります。

トランザクションブロックが広すぎたり、他のファイルで貼ってあると、どこの整合性を保とうとしているのかが分かりづらくなります。

また、他のファイルからSubscriptionCreateServiceを呼び出したときに呼び出し元でトランザクションを貼るのを忘れてしまうといったことも起こりえます。

処理が複雑になるとトランザクションがネストしてしまいますが、ActiveRecord::Rollback以外の例外が起きたときは一番外側のトランザクションまでロールバックされるので、整合性の担保は保証されるでしょう。

ActiveRecord::Rollbackについての挙動はこちらの記事をどうぞ!

Railsでのトランザクションの貼り方について

トランザクションの貼り方について補足をしておきます。

Railsではトランザクションは以下のようにも貼ることができます。

def create
  Subscription.transaction do
    subscription = Subscription.create(subscription_params)
    SubscriptionLogService.execute!(subscription)
  end
end

# 保存するモデルと異なってもOK
def create
  User.transaction do
    subscription = Subscription.create(subscription_params)
    SubscriptionLogService.execute!(subscription)
  end
end

ただし、当然といえば当然ですが上の例でいうとsubscriptionsテーブルとusersテーブルが異なるデータベースにある場合はトランザクションを貼ることはできません。

まとめ

今回の記事の内容をまとめると、トランザクションを貼る位置はデータの整合性を担保したい最小単位で貼るのがいいということでした。

分かりにくいやアドバイス等ありましたらコメントくださると幸いです。では!
Twitterもやってますので、フォローしていただけるとうれしいです。

ABOUT ME
sakai
三重出身の28歳。前職はメーカーで働いていて、プログラミングスクールに通って未経験からWeb業界に転職しました。Railsをメインで使っていて、AWSも少しできます。音楽を聞くこととYoutubeを見るのが好きです。最近はへきトラ劇場にハマってます