business-logic-coder について
Rails 開発者向けに、単発の操作とライフサイクルを持つ状態管理として業務ロジックを構成するスキルです。ActiveInteraction、AASM、トランザクション境界、分離レベル、デッドロック対策を扱います。
主な機能
- 01業務ロジックパターンの選択指針
- 02ActiveInteraction による型付き操作
- 03AASM による状態遷移管理
- 04トランザクション境界と分離の指針
- 05デッドロック対策とアンチパターン
活用シーン
- 01登録、注文、決済操作の実装
- 02注文やサブスクリプションの状態管理
- 03操作と状態遷移の組み合わせ
- 04複数段階のデータベース処理の設計
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ majesticlabs-dev/ majestic-marketplace --skill business-logic-coderインストール後、Claude Code で/business-logic-coderを入力して読み込まれたか確認します。
同梱ファイルreferences/aasm-patterns.mdreferences/active-interaction.md
詳細
ビジネスロジックのパターン
Rails における構造化されたビジネスロジックのオーケストレーターです。
スキルの使い分け
| 必要なこと | スキル | 使用する場面 |
|---|---|---|
| 型付きの操作 | active-interaction-coder | ビジネス操作の作成、サービスオブジェクトのリファクタリング |
| ステートマシン | aasm-coder | ワークフローの実装、状態遷移の管理 |
それぞれの使いどころ
ActiveInteraction
一度だけ実行される「操作」に使用します。
- ユーザー登録
- 注文の確定
- 決済処理
- データのインポート
- レポートの生成
# Example: One-time operation with typed inputs
outcome = Users::Create.run(email: email, name: name)
AASM
ライフサイクルを持つ「状態管理」に使用します。
- 注文ステータス(保留中 → 決済済み → 発送済み)
- サブスクリプションの状態(トライアル → 有効 → キャンセル済み)
- ドキュメントのワークフロー(下書き → レビュー → 公開済み)
- タスクのステータス(未着手 → 進行中 → 完了)
# Example: Stateful entity with transitions
order.pay! # pending → paid
order.ship! # paid → shipped
組み合わせたパターン
多くの場合、組み合わせて使用します。インタラクションによって状態遷移をトリガーします。
module Orders
class Process < ActiveInteraction::Base
object :order, class: Order
def execute
order.process! # AASM transition
fulfill_order(order)
order
end
end
end
セットアップ
# Gemfile
gem "active_interaction", "~> 5.3"
gem "aasm", "~> 5.5"
クイックリファレンス
ActiveInteraction:
.run- 結果を返します(.valid?で確認します).run!- 失敗時に例外を発生させますcompose- ネストされたインタラクションを呼び出します
AASM:
.event!- 状態遷移を実行するか、例外を発生させます.event- 状態遷移を実行するか、false を返します.may_event?- 状態遷移が有効かどうかを確認します.aasm.events- 使用可能なイベントを一覧表示します
トランザクション境界
ビジネスロジックで複数ステップのデータベース操作を行う場合は、適切なトランザクション境界を設定してください。
アトミックな操作
# PROBLEM: Partial failure leaves inconsistent state
def transfer_funds(from, to, amount)
from.update!(balance: from.balance - amount)
to.update!(balance: to.balance + amount) # May fail!
end
# SOLUTION: Transaction wrapper with locking
def transfer_funds(from, to, amount)
ActiveRecord::Base.transaction do
from.lock!
to.lock!
from.update!(balance: from.balance - amount)
to.update!(balance: to.balance + amount)
end
end
分離レベル
# Default: READ COMMITTED (each query sees latest committed data)
# REPEATABLE READ: All reads see same snapshot
ActiveRecord::Base.transaction(isolation: :repeatable_read) do
# Consistent reads for reports
end
# SERIALIZABLE: Full isolation (may cause serialization failures)
ActiveRecord::Base.transaction(isolation: :serializable) do
# Strictest isolation, retry on failure
end
デッドロックの防止
ルール:
- 一貫した順序(ID の昇順)でレコードをロックします
- トランザクションを短く保ちます
- トランザクション内でユーザー入力を受け付けません
- トランザクション内で外部 API を呼び出しません
# PROBLEM: Deadlock risk (inconsistent lock order)
def swap_owners(item_a, item_b)
transaction do
item_a.lock! # Thread 1 locks A
item_b.lock! # Thread 2 locks B -> deadlock!
end
end
# SOLUTION: Consistent lock order
def swap_owners(item_a, item_b)
items = [item_a, item_b].sort_by(&:id)
transaction do
items.each(&:lock!)
# Safe: always locks lower ID first
end
end
トランザクションのアンチパターン
| アンチパターン | 問題 | 解決策 |
|---|---|---|
| 長時間のトランザクション | ロックの競合 | 小さな単位に分割します |
| 内部での API 呼び出し | タイムアウトにより DB がブロックされます | トランザクションの外で呼び出してから、トランザクションを実行します |
| 内部でのユーザー入力 | ロックが無期限に続きます | 先に検証し、最後にトランザクションを実行します |
| ネストされたトランザクション | セーブポイントの扱いが複雑になります | requires_new: true を明示的に使用します |
ネストされたトランザクション
# PROBLEM: Inner rollback doesn't work as expected
transaction do
create_order!
transaction do # This is a savepoint, not new transaction
charge_card! # Failure rolls back to savepoint only
end
end
# SOLUTION: Explicit new transaction
transaction do
create_order!
transaction(requires_new: true) do
charge_card! # Failure rolls back only this block
end
end
関連スキル
event-sourcing-coder- 状態遷移からドメインイベントを記録します
コメント
Markdown 対応