- exeは、製品ロジックの各所で決済APIを直接呼び出す代わりに、状態変化を**請求可能な事実(billable facts)**として記録し、確定した状態をStripeと調整する
- 従来の構造では、データベーストランザクションと決済API呼び出しが絡み合い、部分的な失敗、異常なサブスクリプション状態、決済拒否のような例外が製品フロー全体を揺るがしていた
- チームの席数が追加されると状態をdirtyとしてマークし、後続のワーカーがビジネスルールに従って数量を計算し、変更があった場合にのみStripeのサブスクリプション数量を更新する
- 決済を分離すれば、新規チームメンバーのオンボーディングは決済コードに依存せず、席数計算ルールを変えても招待や参加フローに影響しない
- 同じ調整構造を、アクティブVMやディスク使用量のような従量課金やiOSのアプリ内課金にも適用し、製品イベントは維持したまま決済プロバイダーごとの連携だけを差し替えられる
製品フローから決済ロジックを分離する
- 決済ロジックが一般的なビジネスロジックと混在すると、請求が必要なすべての重要経路に関連コードが広がり、価格体系も脆くなって変更しづらくなる
- exeは、1人が決済知識を独占するのではなく、誰でも関連コードを変更できるようにしつつ、複雑な例外は専門担当者が処理する方式を志向している
- 初期のチーム席数課金は、招待の受諾から決済までが1つの大きなフローとして束ねられていた
- ユーザーが招待を受け入れ、アカウントを確認した後にチームへ参加する
- 共有VMへのアクセス権と、料金プランに応じた計算資源が割り当てられる
- この過程で決済APIも呼び出していた
- このようにデータベース変更と外部API呼び出しを結合すると、片方だけが成功する部分的な失敗が発生しうる
- チームのサブスクリプション状態が異常になることがある
- 追加席数に対する決済が拒否されることがある
- 例外が積み重なるほど、全体構造は脆弱になる
請求可能な事実と事後調整
- 請求可能な事実は、特定の状態が変化したことを示す原子的な作業である
- まず製品ロジックを実行し、リソースの新しい状態を確定する
- その後、確定した事実をもとに決済プロバイダーの状態を調整する
- Stripeに必要なのは、その状態に至る過程ではなく最終的な数量だけである
-
チーム席数の調整
- 招待が受諾されると、チーム席数の状態をdirtyとしてマークする
- 後続のワーカーがdirty状態を検知し、ビジネスルールに従って席数の増減を計算する
- 数量が変わった場合にのみ、Stripeのサブスクリプション数量を更新する
- チームメンバー追加と決済コードが分離されるため、招待フローを書き直しても課金が一緒に壊れず、席数計算方式も独立して変更できる
-
従量課金とアプリ内課金
- 同じ調整プロセスは、あらゆる従量課金に適用される
- システムがアクティブVMとディスク使用量に関する事実を記録する
- メータリングワーカーがそれを決済プロバイダーの状態と調整する
- 新しい課金方式を追加しても事実そのものは維持され、各APIとどう調整するかだけが変わる
- iOSアプリも、誰かがアプリ内課金でサブスクライブしたという事実だけを渡し、実際の決済状態はその後に調整する
- 決済構造は、異なるメンバーでも扱える領域となり、招待フローのような製品機能変更が課金システムを損なう可能性も低くなる
- 同じ調整プロセスは、あらゆる従量課金に適用される
1件のコメント
Lobste.rsの意見
記事の核心である変更分を非同期に検知して処理する方式は、結合度を下げ、副作用を実装するのに適している。
しかしLLMは、コードがあちこちに広がる現象を防ぐというより加速するツールなので、アーキテクチャ上の負債になりやすい。理解する速度より速く生成されるコードをどうレビューできるのかも疑問だが、Exeがコードレビューすらしていないというくだりはさらに怖い。
このアーキテクチャは興味深いものの、記事の前半で提示した問題をどう解決するのかは不明。決済が拒否されると、すべてのリソースの支払いを保証するというより、未払いのリソースを先に提供することになりそう。
請求ワーカーが
declinedという事実を発行してリソースを回収することもできるが、きれいな一方向フローと比べると循環構造になる。Exeのように月次で請求するコンピューティングサービスには合うだろうが、物理機器を発送したり、他サービスのシートを再販売したりする事業では受け入れにくい妥協案。ただしAPI呼び出しやデータベーストランザクションの失敗、異常なサブスクリプション状態、シート決済の拒否をどう扱うかには直接答えていない。リファクタリングで分析収集が壊れたり、特定の行を変更状態として表示しなくなったり、新しい経路で変更表示を漏らしたりする問題もそのまま残る。
シート数・無料枠の制限、最大支出額の設定、プリペイド残高の差し引きのように、製品内部に存在する請求関連の状態も解決できない。製品サービスがイベントを発行し、請求サービスがコンプライアンス状態を製品へ反映し直すことはできるが、そうすると状態を持つ分散システムに2つのアクターが生まれる
Stripeが数字を1つだけ欲しがっているように見えるが、一定規模では決済の明細項目まで提供すると、インターチェンジフィーを削減し、承認率を高められる
Exeがコードレビューをしない方式は新鮮。だとすると、リリースとテストはどのように運用しているのか気になる