目次を表示する

共通基盤の設計軸 2026 ─ 抽象・責務・非機能要件を設計する 15 章

責務の境界 ─ Thinnest Viable Platform

責務の境界 ─ Thinnest Viable Platform

「共通基盤を作る」と決まった瞬間、最初に問われるのが 何を提供し、何を提供しないか。これを誤ると、作りすぎて使われない基盤 か、薄すぎて意味のない基盤 のどちらかになる。

TVP(Thinnest Viable Platform)

Team Topologies の Thinnest Viable Platform ── Skelton と Pais が提示した概念:

A TVP is a careful balance between keeping the platform small and ensuring that the platform is helping to accelerate and simplify software delivery for teams building on the platform.

最小限から始めるプラットフォーム。「Wiki ページ 1 枚でも TVP になりうる」とまで Skelton は言う ── 「うちの基盤はこのクラウドのこのサービスを使っている」という記述だけでも、それが意思決定の負荷を下げるなら立派な TVP。

graph LR
  Stage1[TVP v0:<br/>Wiki ページ 1 枚]
  Stage2[TVP v1:<br/>+ 最小 API + Doc]
  Stage3[TVP v2:<br/>+ 監視 / SDK / Self-service]
  Stage4[TVP v3:<br/>+ Multi-tenant / Quota]

  Stage1 --> Stage2 --> Stage3 --> Stage4

  Note1[「いつ伸ばすか」を、利用者の<br/>cognitive load を見て決める]

  style Stage1 fill:#e1ffe1
  style Stage4 fill:#ffe1e1

TVP の判断基準

「最小限」は何で決まるか。Team Topologies が示す原則:

The platform should reduce the cognitive load on stream-aligned teams.

利用者の認知負荷を下げているか。これが境界の判断基準。

例:通知基盤の TVP

段階提供するもの利用者の認知負荷
TVP v0「SES を使ってください」と WikiSES API を覚える、IAM 設定、テンプレート管理、retry 処理を全部自分で
TVP v1notify(to, subject, body) のラッパー APIretry / IAM / ログ収集を基盤側で吸収
TVP v2+ テンプレート管理、配信スケジューラテンプレート系もカプセル化
TVP v3+ multi-channel(SMS / Push / Slack)チャネル選択も抽象化

v3 を最初から作ろうとしない。利用者が困っていない機能は提供しない。作る前に、それを作らないと困る利用者が複数いるか を確認する。

責務を「持たない」判断

責務を引くというのは、何を提供するか を決めるのと同時に、何を提供しないか を決めること。

提供しないと決める典型

機能なぜ提供しないか
完全に汎用な ORM利用者ドメインに踏み込みすぎる、Aggregate に侵入する
業務ロジックの実装利用者ドメインの責務、基盤がやると Conway 違反
UI コンポーネントデザインは利用者ドメインの責務
巨大なデフォルト値テーブル利用者の文脈を勝手に決める

提供するか迷ったときの 3 つの問い

graph TB
  Start[新機能 X を提供すべきか?]
  Start --> Q1{複数の利用者が<br/>困っているか?}
  Q1 -->|No| NotYet[作らない / 作るな]
  Q1 -->|Yes| Q2{利用者ドメインの<br/>責務に踏み込まないか?}
  Q2 -->|踏み込む| NotYet2[作らない / Conway 違反]
  Q2 -->|踏み込まない| Q3{基盤側の方が<br/>運用 / 専門性で優位か?}
  Q3 -->|No| NotYet3[利用者に任せる]
  Q3 -->|Yes| Make[作る]

  style NotYet fill:#ffe1e1
  style NotYet2 fill:#ffe1e1
  style NotYet3 fill:#ffe1e1
  style Make fill:#e1ffe1

3 つ全部 Yes でないと、提供を保留する。

「責務を奪わない」設計

利用者の責務を 奪う と、利用者は判断できなくなる。

❌ 責務を奪う例:認証基盤が「セッション管理」まで握る

// 認証基盤が session 管理まで提供
authService.startSession(userId);      // ← これが基盤の責務
authService.endSession(userId);
authService.isSessionActive(userId);

問題:session の意味は利用者ドメインで違う

  • A サービス:1 端末 1 セッション
  • B サービス:複数端末 OK
  • C サービス:30 分でセッション切断
  • D サービス:永続セッション

基盤が握ると、利用者全員が 基盤の決めた session モデルに縛られる。

✅ 責務を返す例:認証基盤は「トークン発行」まで

// 基盤は token 発行までで止める
const token = await authService.issueToken({ userId, expiresIn: '...' });

// session 管理は利用者ドメイン側で
sessionStore.save(userId, token, customRules);

境界を切って、利用者の判断空間を残す。これが TVP の本質。

“Boring API” の感覚

新しい機能を提供するとき、派手な機能 よりも 退屈で信頼できる API を優先する。

Sharp / 派手Boring / 退屈
自動マジック明示的な引数
「賢い」デフォルト利用者が知っている挙動
設定豊富限定された選択肢
最新トレンド技術5 年使える枯れた技術

退屈な API は、理解しやすいデバッグしやすい長期維持できる。共通基盤は 数年〜十年単位で生きる ことが多いため、Boring を選ぶ価値が大きい。

TVP の “成長” は段階的に

TVP は最初の状態であって、終着点ではない。利用者の成熟と共に成長する:

graph LR
  V0[v0: Wiki] --> V1[v1: 最小 API]
  V1 --> V2[v2: SDK + Doc]
  V2 --> V3[v3: 監視 + Self-service]
  V3 --> V4[v4: Multi-tenant + Quota]
  V4 --> V5[v5: Marketplace + 拡張]

  Note1[一度提供した機能は<br/>引っ込めにくい]

  style V0 fill:#e1ffe1
  style V5 fill:#fff4e1

注意:一度提供した機能は引っ込めにくい。利用者が依存し始めると、deprecate に年単位かかる。だから 足して引けない性質を考慮し、足すときは慎重に。これは第 12 章で深掘りする。

この章の要点

  • TVP(Thinnest Viable Platform)= 最小限から始めるプラットフォーム
  • Wiki 1 枚でも TVP になりうる
  • 判断基準は「利用者の認知負荷を下げているか
  • 提供すると決める前に 3 つの問い:①複数の利用者が困るか ②利用者ドメインに踏み込まないか ③基盤側の方が優位か
  • 責務を奪わない設計が重要。境界を切って利用者の判断空間を残す
  • Boring API(派手より信頼できる)を優先
  • TVP は段階的に成長。一度提供すると引っ込めにくい

次章への問いかけ

「最小から始める」のは分かった。だがどこまで抽象化するかは別の問題だ。

利用者に callback を書かせる のか、利用者は宣言だけ すれば動くのか。次章で Pit of Success という設計哲学から、抽象度の選択を扱う。