mcpField
A
architecture-patterns

architecture-patterns

github.com/miles990開発26279 回インストール41 週間前に更新
リポジトリ

architecture-patterns について

ソフトウェアアーキテクチャを選び、発展させる開発者向けのリファレンススキルです。各種パターンや選定基準、トレードオフ、よくある落とし穴を扱います。

主な機能

  1. 01モノリスとモジュラーモノリスの指針
  2. 02マイクロサービスとサーバーレスのパターン
  3. 03イベント駆動設計パターン
  4. 04クリーンアーキテクチャとヘキサゴナルアーキテクチャ
  5. 05DDDと移行パターン

活用シーン

  1. 01新規アプリケーションのアーキテクチャ選定
  2. 02サービス境界とデプロイの計画
  3. 03レガシーシステムの段階的な移行

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/miles990/claude-software-skills --skill architecture-patterns

インストール後、Claude Code で/architecture-patternsを入力して読み込まれたか確認します。

詳細

アーキテクチャパターン

概要

アーキテクチャパターンは、ソフトウェアシステムを構成するための実証済みの解決策です。適切なアーキテクチャを選ぶことは、スケーラビリティ、保守性、チームの生産性にとって重要です。

パターン

モノリシックアーキテクチャ

説明:アプリケーションのすべての機能を含む、単一のデプロイ可能なユニットです。

主な特徴:

  • デプロイと開発が簡単
  • データベースとメモリを共有
  • デバッグが容易

ユースケース:

  • MVPやスタートアップ
  • 小規模チーム(開発者10人未満)
  • シンプルなドメインロジック

ベストプラクティス:

src/
├── modules/          # Feature-based organization
│   ├── users/
│   ├── orders/
│   └── products/
├── shared/           # Cross-cutting concerns
└── infrastructure/   # External services

マイクロサービスアーキテクチャ

説明:独立してデプロイ可能なサービスで構成される分散システムです。

主な特徴:

  • 独立したデプロイとスケーリング
  • サービスごとに異なるテクノロジーを採用可能
  • 障害の分離

ユースケース:

  • 自律性を必要とする大規模チーム
  • 明確な境界を持つ複雑なドメイン
  • 高いスケーラビリティが求められる場合

主なコンポーネント:

コンポーネント目的ツール
API Gateway入口、ルーティングKong, AWS API Gateway
Service Discoveryサービス登録Consul, Kubernetes DNS
Config Management設定の一元管理Spring Cloud Config, Consul
Circuit BreakerフォールトトレランスResilience4j, Hystrix

ベストプラクティス:

  1. ビジネス機能を中心に設計する
  2. データ管理を分散化する
  3. 障害を想定して設計する
  4. デプロイを自動化する

イベント駆動アーキテクチャ

説明:イベントを介して通信するシステムです。

主なパターン:

パターン説明ユースケース
Event Sourcing状態をイベントとして保存監査証跡、時間軸に沿ったクエリ
CQRS読み取りモデルと書き込みモデルを分離読み取り負荷の高いワークロード
Saga分散トランザクションサービスをまたぐワークフロー

イベントソーシングの例:

// Events are the source of truth
interface OrderEvent {
  id: string;
  type: 'OrderCreated' | 'ItemAdded' | 'OrderShipped';
  timestamp: Date;
  payload: unknown;
}

// Rebuild state from events
function rebuildOrder(events: OrderEvent[]): Order {
  return events.reduce((order, event) => {
    switch (event.type) {
      case 'OrderCreated': return { ...event.payload };
      case 'ItemAdded': return { ...order, items: [...order.items, event.payload] };
      case 'OrderShipped': return { ...order, status: 'shipped' };
    }
  }, {} as Order);
}

サーバーレスアーキテクチャ

説明:サーバーを管理せずに、クラウドが管理する実行環境を利用します。

主な特徴:

  • 実行ごとの従量課金
  • ゼロまでの自動スケーリング
  • 運用負荷の軽減

考慮事項:

項目影響
コールドスタート初回呼び出し時に100ms~2sの遅延
タイムアウト通常、最大実行時間は15~30分
状態外部ストレージを使用する必要がある
ベンダーロックインプラットフォーム固有の機能

ベストプラクティス:

  1. 関数を小さく、目的に絞る
  2. 依存関係を最小限にする
  3. データベースに接続プーリングを使用する
  4. 適切なエラー処理を実装する

クリーンアーキテクチャ

説明:ドメインを中心に置き、依存関係を逆転させたアーキテクチャです。

レイヤー構造:

┌──────────────────────────────────────┐
│           Frameworks & Drivers       │  ← External (DB, Web, UI)
├──────────────────────────────────────┤
│           Interface Adapters         │  ← Controllers, Gateways
├──────────────────────────────────────┤
│           Application Business       │  ← Use Cases
├──────────────────────────────────────┤
│           Enterprise Business        │  ← Entities, Domain Rules
└──────────────────────────────────────┘

依存関係のルール:依存関係は内側を向きます。内側のレイヤーは外側のレイヤーについて何も知りません。


ドメイン駆動設計(DDD)

説明:ビジネスドメインに沿ったアーキテクチャです。

戦略的パターン:

パターン目的
境界づけられたコンテキスト明確なドメイン境界
コンテキストマップコンテキスト間の関係
ユビキタス言語共有された語彙

戦術的パターン:

パターン目的
エンティティ識別子を持つオブジェクト
値オブジェクト不変の記述子
集約整合性の境界
リポジトリコレクションのような永続化
ドメインイベント発生した出来事

選択ガイド

START
  │
  ├─ Team size < 10? ──────────────────→ Monolith
  │
  ├─ Need independent deployments? ────→ Microservices
  │
  ├─ Audit trail required? ────────────→ Event Sourcing
  │
  ├─ Variable/unpredictable load? ─────→ Serverless
  │
  ├─ Complex business logic? ──────────→ Clean Architecture + DDD
  │
  └─ Default ──────────────────────────→ Modular Monolith

よくある落とし穴

1. 早すぎるマイクロサービス化

問題:シンプルなアプリケーションで最初からマイクロサービスを採用する 解決策:モノリシックから始め、境界が明確になったらサービスを切り出す

2. 分散モノリス

問題:同時にデプロイする必要があるマイクロサービス 解決策:明確なAPI契約を設け、サービスが真に独立していることを確認する

3. データ境界の無視

問題:サービス間でデータベースを共有する 解決策:各サービスが自身のデータを所有し、同期にはイベントを使用する


ヘキサゴナルアーキテクチャ(ポートとアダプター)

説明: ポート(インターフェース)とアダプター(実装)を通じて、外部関心事からアプリケーションコアを分離します。

構造:

┌─────────────────────────────────────────────────────────────┐
│                      Driving Adapters                       │
│    (REST API, CLI, GraphQL, Message Consumer)               │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                    Input Ports                              │
│              (Use Case Interfaces)                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│                   APPLICATION CORE                          │
│              (Domain Logic, Entities)                       │
│                                                             │
├─────────────────────────────────────────────────────────────┤
│                   Output Ports                              │
│           (Repository, Gateway Interfaces)                  │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│                     Driven Adapters                         │
│    (Database, External APIs, Message Publisher)             │
└─────────────────────────────────────────────────────────────┘

TypeScript の例:

// Port (Interface)
interface OrderRepository {
  save(order: Order): Promise<void>;
  findById(id: string): Promise<Order | null>;
}

// Adapter (Implementation)
class PostgresOrderRepository implements OrderRepository {
  constructor(private db: Database) {}

  async save(order: Order): Promise<void> {
    await this.db.query('INSERT INTO orders...', [order]);
  }

  async findById(id: string): Promise<Order | null> {
    const row = await this.db.query('SELECT * FROM orders WHERE id = $1', [id]);
    return row ? this.toDomain(row) : null;
  }
}

// Use Case (Application Core)
class CreateOrderUseCase {
  constructor(private orderRepo: OrderRepository) {} // Depends on Port, not Adapter

  async execute(input: CreateOrderInput): Promise<Order> {
    const order = new Order(input);
    await this.orderRepo.save(order);
    return order;
  }
}

利点:

  • 実装(DB、外部サービス)を簡単に差し替え可能
  • 高いテスト容易性(ポートをモック化)
  • フレームワークに依存しないドメインロジック

モジュラーモノリス

説明: 厳格なモジュール境界を持つモノリスで、将来的なマイクロサービス抽出に備えます。

主な特徴:

  • モジュールは定義されたインターフェースを介して通信する
  • 各モジュールが自身のデータを所有する
  • 単一ユニットとしてデプロイすることも、抽出することも可能

構造:

src/
├── modules/
│   ├── users/
│   │   ├── api/           # Public API of module
│   │   │   └── UserService.ts
│   │   ├── internal/      # Private implementation
│   │   │   ├── UserRepository.ts
│   │   │   └── UserEntity.ts
│   │   └── index.ts       # Only exports public API
│   ├── orders/
│   │   ├── api/
│   │   │   └── OrderService.ts
│   │   ├── internal/
│   │   └── index.ts
│   └── shared/            # Cross-cutting utilities
├── infrastructure/
│   ├── database/
│   ├── messaging/
│   └── http/
└── main.ts

モジュール間通信のルール:

// ✅ Good: Use public API
import { UserService } from '@modules/users';
const user = await userService.getById(id);

// ❌ Bad: Direct access to internal
import { UserRepository } from '@modules/users/internal/UserRepository';

強制:

// eslint rules or ts-paths to prevent internal imports
{
  "rules": {
    "no-restricted-imports": ["error", {
      "patterns": ["@modules/*/internal/*"]
    }]
  }
}

ストラングラーフィグパターン

説明: トラフィックを新しい実装にルーティングすることで、レガシーシステムを段階的に置き換えます。

移行プロセス:

Phase 1: Facade
┌─────────┐     ┌─────────┐     ┌─────────────┐
│ Client  │────→│ Facade  │────→│ Legacy      │
└─────────┘     └─────────┘     │ System      │
                                └─────────────┘

Phase 2: Partial Migration
┌─────────┐     ┌─────────┐     ┌─────────────┐
│ Client  │────→│ Facade  │──┬─→│ Legacy      │
└─────────┘     └─────────┘  │  └─────────────┘
                             │  ┌─────────────┐
                             └─→│ New System  │
                                └─────────────┘

Phase 3: Complete Migration
┌─────────┐     ┌─────────┐     ┌─────────────┐
│ Client  │────→│ Facade  │────→│ New System  │
└─────────┘     └─────────┘     └─────────────┘

実装:

class PaymentFacade {
  constructor(
    private legacyPayment: LegacyPaymentService,
    private newPayment: NewPaymentService,
    private featureFlags: FeatureFlags
  ) {}

  async processPayment(payment: Payment): Promise<Result> {
    // Gradually migrate traffic
    if (this.featureFlags.isEnabled('new-payment-system', payment.userId)) {
      return this.newPayment.process(payment);
    }
    return this.legacyPayment.process(payment);
  }
}

Backend for Frontend(BFF)

説明: フロントエンドの種類(Web、モバイルなど)ごとに専用のバックエンドを用意します。

構造:

                    ┌─────────────┐
                    │ Web Client  │
                    └──────┬──────┘
                           │
                    ┌──────▼──────┐
                    │  Web BFF    │
                    └──────┬──────┘
                           │
       ┌───────────────────┼───────────────────┐
       │                   │                   │
┌──────▼──────┐    ┌──────▼──────┐    ┌──────▼──────┐
│ User Service│    │Order Service│    │Product Svc  │
└─────────────┘    └─────────────┘    └─────────────┘
       │                   │                   │
       └───────────────────┼───────────────────┘
                           │
                    ┌──────▼──────┐
                    │ Mobile BFF  │
                    └──────┬──────┘
                           │
                    ┌──────▼──────┐
                    │Mobile Client│
                    └─────────────┘

利点:

  • 各クライアントに最適化されたペイロード
  • クライアント固有の認証
  • フロントエンドごとの独立したデプロイ
  • オーバーフェッチの削減

使用すべき場面:

シナリオ推奨
単一のクライアントタイプBFF を省略
同じニーズを持つ Web + モバイル単一の API ゲートウェイ
プラットフォームごとに異なる UX個別の BFF
フロントエンドごとに複数チーム専用の BFF

アーキテクチャパターンの比較

パターン複雑さスケーラビリティチーム規模最適な用途
モノリス低垂直小規模(2〜10)MVP、シンプルなアプリ
モジュラーモノリス中垂直中規模(5〜20)成長中のアプリ
マイクロサービス高水平大規模(20 以上)複雑なドメイン
サーバーレス中自動任意イベント駆動、変動する負荷
イベント駆動高水平中〜大規模非同期ワークフロー

アーキテクチャ決定記録(ADR)テンプレート

アーキテクチャを選択する際は、決定事項を記録します:

# ADR-001: Choose Modular Monolith

## Status
Accepted

## Context
- Team of 8 developers
- MVP deadline in 3 months
- Uncertain about domain boundaries
- Limited DevOps resources

## Decision
Adopt Modular Monolith with strict boundaries

## Consequences
### Positive
- Faster initial development
- Simpler deployment
- Can extract services later

### Negative
- Single point of failure
- Scaling limited to vertical
- Need discipline for module boundaries

## Alternatives Considered
1. Microservices - Too complex for team size
2. Traditional Monolith - No path to scale

進化の道筋

┌─────────────────────────────────────────────────────────────────┐
│                    Architecture Evolution                        │
│                                                                 │
│   Monolith ──→ Modular Monolith ──→ Microservices              │
│      │              │                     │                     │
│      │              │                     ▼                     │
│      │              │            Event-Driven / CQRS            │
│      │              │                     │                     │
│      ▼              ▼                     ▼                     │
│  [Simple]     [Growing]            [Complex/Scale]              │
│                                                                 │
│   Tip: Don't skip steps. Each stage teaches domain boundaries. │
└─────────────────────────────────────────────────────────────────┘

避けるべきアンチパターン

1. 大きな泥団子(Big Ball of Mud)

症状: 明確な構造がなく、すべてがすべてに依存している 修正: モジュール境界を導入し、クリーンアーキテクチャの原則を適用する

2. 金のハンマー

症状: すべてのプロジェクトで同じアーキテクチャを使用する 修正: 要件を評価し、意思決定ガイドを使用する

3. 偶発的な複雑さ

症状: アーキテクチャがドメインの要求よりも複雑になっている 修正: シンプルに始め、必要な場合にのみ複雑さを追加する

4. 履歴書駆動開発

症状: 問題を解決するためではなく、学習のために技術を選定する 修正: アーキテクチャをチームのスキルとプロジェクトのニーズに合わせる

5. ベンダーロックイン

症状: コアロジックがクラウドプロバイダーに密結合している 修正: ヘキサゴナルアーキテクチャを使用し、ベンダー固有のコードを抽象化する


パターン別のパフォーマンスに関する考慮事項

パターンレイテンシスループットコールドスタート
モノリス低高該当なし
マイクロサービス中(ネットワーク)高(分散)該当なし
サーバーレス変動自動スケール100ms〜2s
イベント駆動高め(非同期)非常に高状況による

パターン別のテスト戦略

モノリス
Unit Tests → Integration Tests → E2E Tests
    70%           20%              10%
マイクロサービス
Unit Tests → Contract Tests → Integration → E2E
    60%           20%           15%         5%

// Contract Test Example (Pact)
const provider = new Pact({ consumer: 'OrderService', provider: 'UserService' });
await provider.addInteraction({
  state: 'user exists',
  uponReceiving: 'get user request',
  withRequest: { method: 'GET', path: '/users/123' },
  willRespondWith: { status: 200, body: { id: '123', name: 'John' } }
});
イベント駆動
  • イベントプロデューサーとコンシューマーを独立してテストする
  • イベントスキーマの検証を使用する
  • サーガ/ワークフローのオーケストレーションをテストする

関連スキル

  • [[api-design]] - サービス間通信のための API 設計
  • [[system-design]] - 大規模システムに関する考慮事項
  • [[devops-cicd]] - 各パターンのデプロイ戦略
  • [[data-design]] - 各アーキテクチャのデータベースパターン

コメント