責務の境界 ─ 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 を使ってください」と Wiki | SES API を覚える、IAM 設定、テンプレート管理、retry 処理を全部自分で |
| TVP v1 | notify(to, subject, body) のラッパー API | retry / 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 という設計哲学から、抽象度の選択を扱う。