メインコンテンツまでスキップ

ADR: organization 将来導入と billing 分離戦略

ステータス: Accepted 決定日: 2026-04-15 関連 Issue: #236, #237, #238, #239

1. 背景

リリース後 1 年前後で「複数ユーザーが所属する organization(チーム)」の概念を導入する可能性がある。現時点では導入は未確定だが、v0.3 マイルストーン「スケーラビリティ・組織移行」に候補として存在する。

リリース後に users テーブル中心の所有権構造が固まった後で organization を後付け導入すると、以下のコストが連鎖する。

  • 既存テーブルへの organization_id バックフィル
  • 認可ロジックの総書き換え(「自分のデータか」→「自分の organization のデータか」)
  • Stripe customer の付け替え(user → organization)。既存サブスクの移行は特に重い
  • Better Auth の organization plugin を後から有効化する際のスキーマ衝突

本 ADR では、「organization 導入の有無に関わらず単体で正当化できる設計改善」のみを先回りで行い、organization 特化の先行実装は YAGNI として避ける方針を固定する。

2. 方針サマリ

  1. billing は最初から users と分離するbilling_accounts テーブル)
  2. API 認可は抽象化するcanAccess(actor, resource) ヘルパー)
  3. organization 導入時は Better Auth organization plugin を採用する(独自実装しない)
  4. organization 特化の先行実装は行わないorganizations / members / owner_type カラム等は YAGNI)

3. 所有者マトリクス

各リソースについて「現在の所有者」と「将来 organization 化する可能性」を整理する。

テーブル現在の所有者将来 organization 化備考
users-認証主体。organization に属する側
billingAccounts (旧 userSubscriptions)user✅ 高課金主体は organization 単位が自然
userVideoAccessuser✅ 中課金と連動するため organization 側に寄る可能性
favoriteChannelsuser⚠️ 個人 or 共有どちらもあり得るプロダクト仕様で判断(ユーザーごと or チーム共有)
searchHistoriesuser❌ 低個人の検索履歴。organization 化しない想定
channels / videosなし(グローバル)全ユーザー共有のマスターデータ

4. 決定事項

4.1 billing_accounts 分離(採用)

user_subscriptionsbilling_accounts にリネーム・再設計し、Stripe customer を別テーブルで管理する。

理由:

  • 「認証ユーザー情報」と「課金顧客情報」は本来別ドメイン。users テーブルが Stripe の都合で膨らむのを防ぐ
  • 退会時の扱い: user 削除 ≠ Stripe customer 削除(返金処理・監査ログのため課金レコードは残したい)
  • テスト容易性: 課金ロジックだけ独立してモック・テストできる
  • 複数決済手段の拡張: 将来 Stripe 以外(App Store 等)が増えた時に billing_accounts 側を拡張すれば済む
  • organization 導入時: テーブルの所有者を user → organization に付け替えるだけで済む

スキーマ方針:

  • id (PK), userId (FK, UNIQUE), stripeCustomerId (UNIQUE, nullable), plan, creditBalance, updatedAt
  • owner_type カラムは追加しない(YAGNI。organization 導入時に ALTER で追加する)

詳細: #238

4.2 canAccess 抽象化(採用)

API 層に canAccess(actor, resource) ヘルパーを導入し、リソース所有者チェックを統一する。

理由:

  • ctx.user.id === resource.userId のような直書きがハンドラ・service に散在するのを防ぐ
  • 認可ロジックを一箇所に集約することでテスト容易性・一貫性が向上する
  • 将来 Actor 型を { type: "user" | "organization"; id } に拡張する余地を残せる
  • organization 導入の有無と無関係に責務分離として有効

設計方針:

  • Actor 型は現状 { type: "user"; id: string } のみ
  • 将来 | { type: "organization"; id: string; userId: string } を追加する前提で overload を設計

詳細: #239

4.3 Better Auth organization plugin 採用方針(採用)

organization 機能を実装する際は、Better Auth の organization plugin を採用する。独自実装は行わない。

理由:

  • plugin が organizations / members / invitations テーブルとマイグレーションを提供する
  • 招待フロー・メンバー管理・権限モデルも標準対応されている
  • 独自実装と plugin のスキーマ競合を避けられる

今やること: 特になし。この方針を固定しておくだけで十分(独自 organizations テーブルの先行作成を禁じる意思決定)。

5. 非採用案

5.1 organizations テーブルの先行作成(不採用)

却下理由: Better Auth organization plugin を将来採用する前提に立つと、plugin 提供のスキーマと衝突する。plugin 有効化時に移行が必要になり、先行投資が無駄になる。

5.2 全テーブルへの owner_type カラム先行投入(不採用)

却下理由: organization 導入が未確定のため、使われない可能性がある。未使用カラムは認知コスト・マイグレーションコストを無駄に上げる。必要になった時点で ALTER TABLE で追加すれば済む。

5.3 Better Auth organization plugin の先行有効化(不採用)

却下理由: 有効化すると organizations / members テーブルが生成され、招待フロー等の UI 実装が必要になる。v0.1 では organization UI を出さない方針のため YAGNI。

6. 実行項目(v0.1)

Issue内容状態
#237本 ADR の作成In Progress
#238user_subscriptionsbilling_accounts 再設計Todo
#239認可ヘルパー canAccess 導入Todo

7. 将来の見直しタイミング

以下の条件を満たした時に本 ADR を再評価する。

  • organization 機能の要望が明確になった(Enterprise 顧客等からの具体的な問い合わせ)
  • 有料プラン転換率が想定値を超え、organization 単位での課金ニーズが可視化された
  • Better Auth の organization plugin に破壊的変更があった