LESSON 30分

ストーリー

高橋アーキテクト
先月、取引先の会社がクラウドプロバイダーの突然の値上げで予算を大幅に超過したらしい
あなた
移行すればいいのでは?
高橋アーキテクト
そこが問題だ。プロプライエタリなサービスに深く依存していて、移行には1年と莫大なコストがかかる見積もりだそうだ。これがベンダーロックインの恐ろしさだ
あなた
じゃあ、クラウド固有のサービスは一切使わないほうがいいんですか?
高橋アーキテクト
そうとも限らない。クラウド固有のサービスは生産性が高い。大事なのは、ロックインの”リスクを理解した上で”判断することだ。完全に避けるのは不可能だし、時には受け入れるのが正しい選択のこともある

ベンダーロックインとは

// ロックインの種類
enum LockInType {
  TECHNICAL = "技術的ロックイン",     // プロプライエタリAPI・フォーマット
  DATA = "データロックイン",          // データのエクスポートが困難
  CONTRACTUAL = "契約的ロックイン",   // 長期契約・最低利用量
  SKILL = "スキルロックイン",         // 特定技術の専門家に依存
  PLATFORM = "プラットフォームロックイン", // エコシステム全体への依存
}

interface LockInAssessment {
  service: string;
  provider: string;
  lockInTypes: LockInType[];
  switchingCost: "low" | "medium" | "high" | "very_high";
  alternatives: string[];
  mitigationStrategy: string;
}

// 評価例
const assessments: LockInAssessment[] = [
  {
    service: "AWS Lambda",
    provider: "AWS",
    lockInTypes: [LockInType.TECHNICAL, LockInType.PLATFORM],
    switchingCost: "medium",
    alternatives: ["Google Cloud Functions", "Azure Functions", "Cloudflare Workers"],
    mitigationStrategy: "ビジネスロジックをハンドラから分離し、ポータブルに保つ",
  },
  {
    service: "AWS DynamoDB",
    provider: "AWS",
    lockInTypes: [LockInType.TECHNICAL, LockInType.DATA],
    switchingCost: "very_high",
    alternatives: ["MongoDB", "Cassandra", "ScyllaDB"],
    mitigationStrategy: "Repository パターンでデータアクセス層を抽象化",
  },
  {
    service: "Firebase Authentication",
    provider: "Google",
    lockInTypes: [LockInType.TECHNICAL, LockInType.SKILL],
    switchingCost: "high",
    alternatives: ["Auth0", "Keycloak", "Supabase Auth"],
    mitigationStrategy: "認証インターフェースを抽象化し、アダプタで切り替え可能に",
  },
];

ロックイン回避の設計パターン

Ports & Adapters(Hexagonal Architecture)

// Port: インターフェースを定義
interface StoragePort {
  upload(key: string, data: Buffer): Promise<string>;
  download(key: string): Promise<Buffer>;
  delete(key: string): Promise<void>;
  listObjects(prefix: string): Promise<string[]>;
}

// Adapter: AWS S3 用の実装
class S3StorageAdapter implements StoragePort {
  constructor(private s3Client: S3Client) {}

  async upload(key: string, data: Buffer): Promise<string> {
    await this.s3Client.send(new PutObjectCommand({
      Bucket: process.env.S3_BUCKET,
      Key: key,
      Body: data,
    }));
    return `s3://${process.env.S3_BUCKET}/${key}`;
  }

  async download(key: string): Promise<Buffer> {
    const response = await this.s3Client.send(new GetObjectCommand({
      Bucket: process.env.S3_BUCKET,
      Key: key,
    }));
    return Buffer.from(await response.Body!.transformToByteArray());
  }

  async delete(key: string): Promise<void> { /* ... */ }
  async listObjects(prefix: string): Promise<string[]> { /* ... */ }
}

// Adapter: GCS 用の実装(将来の移行時に追加)
class GCSStorageAdapter implements StoragePort {
  constructor(private storage: Storage) {}
  async upload(key: string, data: Buffer): Promise<string> { /* ... */ }
  async download(key: string): Promise<Buffer> { /* ... */ }
  async delete(key: string): Promise<void> { /* ... */ }
  async listObjects(prefix: string): Promise<string[]> { /* ... */ }
}

// アプリケーション層はPortのみに依存
class FileUploadUseCase {
  constructor(private storage: StoragePort) {}

  async execute(fileName: string, data: Buffer): Promise<string> {
    const key = `uploads/${Date.now()}_${fileName}`;
    return this.storage.upload(key, data);
  }
}

ロックイン受容の判断基準

完全なロックイン回避は非現実的です。受容すべき場合もあります。

## ロックインを受け入れてよい場合

1. **コスト対効果が明確に高い**
   - 抽象化のコスト > ベンダー固有サービスの生産性向上

2. **移行の可能性が低い**
   - 5年以上使い続ける確信がある
   - 業界標準のサービス

3. **代替手段がない**
   - 特定ベンダーのみが提供する独自機能

4. **スタートアップフェーズ**
   - スピードが最優先で、将来の移行コストを許容できる

## ロックインを避けるべき場合

1. **コア・ビジネスロジック**
   - ビジネスの核心はポータブルに保つ

2. **データ格納層**
   - データの移行は常に最もコストが高い

3. **マルチクラウドの要件がある場合**

4. **ベンダーの将来性に不安がある場合**
判断要素受容回避
生産性向上大きい小さい
移行コスト許容範囲致命的
ベンダーの安定性高い不明
代替手段あるない
ビジネスへの影響限定的致命的

ロックイン対策チェックリスト

## 新しいサービス導入時のチェック

- [ ] ロックインの種類を特定したか
- [ ] スイッチングコストを見積もったか
- [ ] 代替手段を調査したか
- [ ] データのエクスポート方法を確認したか
- [ ] 抽象化レイヤーの必要性を判断したか
- [ ] チーム内の合意を得たか
- [ ] 判断理由をADRに記録したか

まとめ

ポイント内容
ベンダーロックイン特定ベンダーへの依存で移行困難になること
種類技術的、データ、契約、スキル、プラットフォーム
回避策Ports & Adaptersパターンで抽象化層を設ける
受容生産性と移行コストのバランスで判断

チェックリスト

  • ベンダーロックインの5つの種類を説明できる
  • Ports & Adaptersパターンでの回避方法を理解した
  • ロックインを受容すべき判断基準を把握した
  • 新サービス導入時のチェックリストを確認した

次のステップへ

次は演習です。ここまで学んだ技術選定のフレームワークを使って、実際に技術選定を行いましょう。


推定読了時間: 30分