LESSON 30分

ストーリー

佐藤CTO
Step 3 でアーキテクチャの選択肢を洗い出した。次はその中から”最適”を選ぶ方法だ
佐藤CTO
ただし注意してほしい。“最適”とは”すべてにおいて最高”という意味ではない。アーキテクチャには必ずトレードオフがある。何かを得れば、何かを犠牲にする。それを定量的に分析して、根拠ある判断を下すことがアーキテクトの仕事だ
佐藤CTO
完璧なアーキテクチャは存在しない。だからこそ、何を優先し、何を妥協するかを明確にする力が求められるんだ

アーキテクチャとトレードオフ

すべてのアーキテクチャ決定にはトレードオフが伴います。これはソフトウェア工学における根本的な事実です。

graph TD
    A["<b>アーキテクチャの鉄則</b><br/><br/>「銀の弾丸は存在しない」<br/>-- フレデリック・ブルックス<br/><br/>すべての設計判断は、あるメリットを<br/>得るために、別のメリットを犠牲にする"]
    style A fill:#f9f9f9,stroke:#333,stroke-width:2px

よくあるトレードオフの軸

トレードオフ一方もう一方
一貫性 vs 可用性強い整合性高い可用性
パフォーマンス vs 保守性最適化されたコード読みやすいコード
柔軟性 vs 複雑性拡張しやすい設計シンプルな設計
開発速度 vs 品質素早いリリース堅牢なテスト
コスト vs スケーラビリティ低い初期コスト高いスケーラビリティ

CAP定理

分散システムにおける最も有名なトレードオフが CAP 定理です。

CAP定理の3要素

graph TD
    C["Consistency<br/>(一貫性)"]
    A["Availability<br/>(可用性)"]
    P["Partition Tolerance<br/>(分断耐性)"]

    C ---|"CP"| P
    C ---|"CA"| A
    A ---|"AP"| P

    style C fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
    style A fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
    style P fill:#fff3e0,stroke:#e65100,stroke-width:2px
要素説明
Consistency(C)すべてのノードが同じデータを返す銀行の残高照会
Availability(A)すべてのリクエストに応答するECサイトの商品表示
Partition Tolerance(P)ネットワーク分断時も動作する地理分散システム

CAP定理: 分散システムでは、C・A・P の3つのうち同時に2つまでしか保証できない。

現実的な選択

分散システムではネットワーク分断は避けられないため、実質的には CPAP の選択になります。

// CP寄りの設計例:金融取引
interface TransactionService {
  // 一貫性を優先:全ノードの同期を待つ
  transfer(from: string, to: string, amount: number): Promise<TransactionResult>;
  // ネットワーク分断時はエラーを返す(可用性を犠牲)
}

// AP寄りの設計例:SNSのタイムライン
interface TimelineService {
  // 可用性を優先:最寄りノードから即座に応答
  getFeed(userId: string): Promise<Post[]>;
  // 少し古いデータが表示される可能性あり(結果整合性)
}

PACELC定理

CAP定理を拡張した PACELC は、分断がない通常時のトレードオフも考慮します。

if (Partition) {
  // 分断時: Availability vs Consistency を選択
  choose(A or C);
} else {
  // 通常時: Latency vs Consistency を選択
  choose(L or C);
}
パターン分断時通常時特徴データベース例
PA/ELA優先L優先高速・結果整合DynamoDB, Cassandra
PA/ECA優先C優先可用性重視-
PC/ELC優先L優先バランス型-
PC/ECC優先C優先厳密な一貫性PostgreSQL, MySQL

Build vs Buy の判断

判断マトリクス

quadrantChart
    title Build vs Buy 判断マトリクス
    x-axis コモディティ --> 高い複雑性
    y-axis コモディティ --> コア競争力に関わる
    quadrant-1 Build(戦略的投資)
    quadrant-2 Build(差別化)
    quadrant-3 Buy(効率化)
    quadrant-4 Buy(専門家に任せる)

判断基準

interface BuildVsBuyDecision {
  // 自社構築を検討すべき場合
  buildWhen: [
    "コア競争力に直結する機能",
    "市場に適切な製品がない",
    "既存製品のカスタマイズコストが高すぎる",
    "データの完全な制御が必要"
  ];

  // 購入/SaaS利用を検討すべき場合
  buyWhen: [
    "コモディティ化された機能(認証、メール送信)",
    "市場に成熟した製品がある",
    "開発・保守コストが購入コストを上回る",
    "専門知識が社内に不足している"
  ];
}

実践例:認証システム

選択肢メリットデメリット
自社構築完全なカスタマイズ、ベンダー非依存開発コスト大、セキュリティリスク
Auth0高機能、セキュリティ実績月額コスト、ベンダーロックイン
Keycloak (OSS)カスタマイズ可能、無料運用コスト、学習コスト

技術的負債というトレードオフ

技術的負債は「意図的に品質を下げて開発速度を上げる」トレードオフです。

技術的負債の4象限(Martin Fowlerの分類)

quadrantChart
    title 技術的負債の4象限(Martin Fowlerの分類)
    x-axis 無謀 --> 慎重
    y-axis 無意識 --> 意図的
    quadrant-1 "今はリリース優先で後でリファクタする"
    quadrant-2 "設計なんて時間の無駄"
    quadrant-3 "レイヤーって何?"
    quadrant-4 "この設計の問題点が今になって分かった"

負債の管理

// 技術的負債の記録例
interface TechnicalDebtRecord {
  id: string;
  description: string;
  reason: string;          // なぜこの負債を許容したか
  impact: "HIGH" | "MEDIUM" | "LOW";
  interestRate: string;    // 放置した場合の影響増大率
  repaymentPlan: string;   // いつ・どう返済するか
  createdAt: Date;
  deadline: Date;          // 返済期限
}

// 例
const debt: TechnicalDebtRecord = {
  id: "TD-042",
  description: "注文サービスにハードコードされたビジネスルール",
  reason: "Q1リリースに間に合わせるため",
  impact: "MEDIUM",
  interestRate: "ルール追加のたびにif文が増加、月1回の頻度",
  repaymentPlan: "Q2にStrategy パターンでリファクタ",
  createdAt: new Date("2025-01-15"),
  deadline: new Date("2025-06-30"),
};

意思決定マトリクスと加重スコアリング

ステップ

  1. 評価基準を定義する
  2. 各基準に重みを付与する(合計100%)
  3. 各選択肢を1-5でスコアリングする
  4. 加重スコアを計算する

実践例:メッセージキュー選定

基準重みRabbitMQApache KafkaAmazon SQS
スループット25%353
運用の容易さ20%325
チームの習熟度20%423
コスト15%434
エコシステム10%453
耐障害性10%355
加重スコア100%3.453.353.75
// 加重スコア計算
interface Criterion {
  name: string;
  weight: number;    // 0.0 ~ 1.0
}

interface EvaluationResult {
  option: string;
  scores: Map<string, number>;  // 基準名 → スコア (1-5)
  weightedScore: number;
}

function calculateWeightedScore(
  criteria: Criterion[],
  scores: Map<string, number>
): number {
  return criteria.reduce((total, criterion) => {
    const score = scores.get(criterion.name) ?? 0;
    return total + criterion.weight * score;
  }, 0);
}

// 使用例
const criteria: Criterion[] = [
  { name: "スループット", weight: 0.25 },
  { name: "運用の容易さ", weight: 0.20 },
  { name: "チームの習熟度", weight: 0.20 },
  { name: "コスト", weight: 0.15 },
  { name: "エコシステム", weight: 0.10 },
  { name: "耐障害性", weight: 0.10 },
];

const sqsScores = new Map([
  ["スループット", 3],
  ["運用の容易さ", 5],
  ["チームの習熟度", 3],
  ["コスト", 4],
  ["エコシステム", 3],
  ["耐障害性", 5],
]);

const sqsResult = calculateWeightedScore(criteria, sqsScores);
// 0.25*3 + 0.20*5 + 0.20*3 + 0.15*4 + 0.10*3 + 0.10*5 = 3.75

注意点

「数字は判断を助けるツールであって、判断そのものではない」と佐藤CTOは注意を促します。

「加重スコアが僅差の場合、数字だけで決めてはいけない。定性的な要素 — チームの士気、戦略的方向性、リスク許容度 — も考慮に入れよう」


まとめ

ポイント内容
トレードオフの不可避性すべてのアーキテクチャ決定にはトレードオフが伴う
CAP定理分散システムではC・A・Pのうち2つまで
PACELC通常時のLatency vs Consistencyも考慮
Build vs Buyコア競争力に関わるかどうかで判断
技術的負債意図的な負債は記録し、返済計画を立てる
意思決定マトリクス基準と重みで定量的に比較する

チェックリスト

  • CAP定理の3要素と実際の選択パターンを説明できる
  • PACELC定理でCAP定理がどう拡張されるか理解した
  • Build vs Buy の判断基準を挙げられる
  • 技術的負債の4象限を理解した
  • 加重スコアリングで選択肢を比較できる

次のステップへ

次は ATAM(Architecture Tradeoff Analysis Method)を学びます。SEI(Software Engineering Institute)が開発した、体系的なトレードオフ分析手法を理解しましょう。


推定読了時間: 30分