背景
PR #232 でトランザクション境界をユースケース層に移した結果、user_id のソースが2箇所になった:
TransactionManager::begin(user_id, operation) — event_set 行に記録される
- 各リポジトリのmutatingメソッド (
create / update / delete / restore / find_or_create_by_name) の user_id 引数 — WHERE句やINSERTに使用される
両者が乖離すると監査記録が不整合になるため、レビュー指摘 (PR #232) を受けて暫定対策としてランタイムチェックを導入済み: PgTransaction が begin 時の UserId を保持し、各mutatingメソッドが冒頭の tx.ensure_user(user_id)? で照合、不一致なら DomainError::Unexpected を返す。
ランタイムチェックは乖離を検出できるが、型レベルで防止はできない。本Issueはレビューで提示されたもう一方の案(シグネチャから user_id を除去し、トランザクションを単一の信頼源にする)を扱う。
提案
mutatingメソッドのシグネチャから user_id 引数を削除する:
// Before
async fn create(&self, tx: &mut Self::Transaction, user_id: &UserId, book: &Book) -> Result<(), DomainError>;
// After
async fn create(&self, tx: &mut Self::Transaction, book: &Book) -> Result<(), DomainError>;
Pg* リポジトリ実装は具体型 PgTransaction を知っているため、tx.user_id() から user を取得してWHERE句・INSERTに使用できる。ドメイントレイトの Self::Transaction は不透明な関連型のままでよく、use_case層への sqlx 漏れは発生しない。誤った user_id を渡すことが型レベルで不可能になり、ensure_user のランタイムチェックは不要になる。
影響範囲
- ドメイントレイト:
BookRepository / AuthorRepository のmutatingメソッド 9件のシグネチャ変更(read系 find_* はプール直アクセスのため user_id 引数を維持 — 非対称性が残る点は設計上のトレードオフ)
- 全mutatingインタラクターの呼び出し箇所
- ユニットテスト: mockallの
expect_*().with(...) / returning(...) の引数が1つ減る(全インタラクターテストに波及)
- インフラDBテスト(
test-with-database)のヘルパー群
PgTransaction::ensure_user と各メソッド冒頭のガード、および test_create_rejects_user_mismatched_transaction の削除
- CLAUDE.md / AGENTS.md の Event Recording セクションと
.agent/plans/ の Decision Log 更新
完了条件
- mutatingメソッドが
user_id 引数を持たず、PgTransaction 由来の user で動作すること
ensure_user ランタイムチェックが削除されていること
cargo fmt --check / cargo clippy --all-targets -- -D warnings / cargo test / cargo test --features test-with-database / E2E がすべて通ること
関連
背景
PR #232 でトランザクション境界をユースケース層に移した結果、user_id のソースが2箇所になった:
TransactionManager::begin(user_id, operation)—event_set行に記録されるcreate/update/delete/restore/find_or_create_by_name) のuser_id引数 — WHERE句やINSERTに使用される両者が乖離すると監査記録が不整合になるため、レビュー指摘 (PR #232) を受けて暫定対策としてランタイムチェックを導入済み:
PgTransactionがbegin時のUserIdを保持し、各mutatingメソッドが冒頭のtx.ensure_user(user_id)?で照合、不一致ならDomainError::Unexpectedを返す。ランタイムチェックは乖離を検出できるが、型レベルで防止はできない。本Issueはレビューで提示されたもう一方の案(シグネチャから user_id を除去し、トランザクションを単一の信頼源にする)を扱う。
提案
mutatingメソッドのシグネチャから
user_id引数を削除する:Pg*リポジトリ実装は具体型PgTransactionを知っているため、tx.user_id()から user を取得してWHERE句・INSERTに使用できる。ドメイントレイトのSelf::Transactionは不透明な関連型のままでよく、use_case層への sqlx 漏れは発生しない。誤った user_id を渡すことが型レベルで不可能になり、ensure_userのランタイムチェックは不要になる。影響範囲
BookRepository/AuthorRepositoryのmutatingメソッド 9件のシグネチャ変更(read系find_*はプール直アクセスのためuser_id引数を維持 — 非対称性が残る点は設計上のトレードオフ)expect_*().with(...)/returning(...)の引数が1つ減る(全インタラクターテストに波及)test-with-database)のヘルパー群PgTransaction::ensure_userと各メソッド冒頭のガード、およびtest_create_rejects_user_mismatched_transactionの削除.agent/plans/の Decision Log 更新完了条件
user_id引数を持たず、PgTransaction由来の user で動作することensure_userランタイムチェックが削除されていることcargo fmt --check/cargo clippy --all-targets -- -D warnings/cargo test/cargo test --features test-with-database/ E2E がすべて通ること関連