Interactive

Facade Pattern

プロンプトに「Facade」を含めることで、ClaudeCodeは複数のサブシステムへの複雑な処理を単一のインターフェースにまとめる実装を生成できます。弊社では、外部API連携、複数サービスの統合、複雑な初期化処理の簡略化など、システムの複雑さをクライアントから隠したい場合にFacadeパターンを使用します。

弊社の推奨ルール

  1. 複雑性の隠蔽 - 複数システムの詳細を単一インターフェースで提供
  2. 依存関係の整理 - クライアントコードの外部依存を最小化
  3. 高レベルAPIの提供 - よく使われる操作を簡単なメソッドで実装
  4. エラーハンドリングの統合 - 複数システムのエラーを統一的に処理
  5. パフォーマンス最適化 - 複数呼び出しをまとめて効率化

ClaudeCodeでの利用

複数サービスの統合

複数のサービス呼び出しを一つのメソッドにまとめたい場合

「複数のサービスを呼び出す処理をFacadeパターンでまとめて」

横断的関心事の統合

認証やロギングなど横断的な処理を統合する場合

「認証、ロギング、データ保存を一つのメソッドにまとめたい」

複数データソースの統一

外部API、データベース、キャッシュを統一インターフェースで扱う場合

「外部API、データベース、キャッシュの処理を統一されたインターフェースで提供するFacade作成」

複数のサービス呼び出しではなくFacadeを使う

複数のサービスやAPIを個別に呼び出すと、クライアントコードが複雑になり、エラーハンドリングも散らばります。

// ❌ 避ける:複数サービスの直接呼び出し
class OrderController {
  async createOrder(orderData: CreateOrderData): Promise<Order> {
    // ユーザー検証
    const user = await this.userService.getUser(orderData.userId);
    // 商品情報取得
    const product = await this.productService.getProduct(orderData.productId);
    // 在庫チェック
    const inventory = await this.inventoryService.checkStock(orderData.productId);

決済処理やその他の処理がコントローラーに散らばっています。

    // 決済処理
    const payment = await this.paymentService.processPayment({...});
    // メール送信、ログ出力など多数の処理...
  }
}

Facadeパターンで処理を統合し、シンプルなインターフェースを提供します。

// ✅ 推奨:Facadeパターン
class OrderFacade {
  constructor(
    private userService: UserService,
    private paymentService: PaymentService,
    // その他のサービス
  ) {}
  
  async createOrder(orderData: CreateOrderData): Promise<Order> {
    // 複雑な処理を段階的に実行
    const validatedData = await this.validateOrderData(orderData);
    const paymentResult = await this.processPayment(validatedData);
    const order = await this.finalizeOrder(validatedData, paymentResult);
    
    return order;
  }
}

各処理は専用のプライベートメソッドに分割されます。バリデーションでは複数のサービスを並列で呼び出してパフォーマンスを最適化します。

// バリデーション処理
private async validateOrderData(orderData: CreateOrderData) {
  const [user, product, inventory] = await Promise.all([
    this.userService.getUser(orderData.userId),
    this.productService.getProduct(orderData.productId),
    this.inventoryService.checkStock(orderData.productId, orderData.quantity)
  ]);

取得したデータの検証と結果の返却を行います。

  if (!user?.isActive) throw new Error('User is not active');
  if (!product) throw new Error('Product not found');
  
  return { user, product, inventory, orderData };
}

クライアントコードは非常にシンプルになります。

// クライアントコードは非常にシンプル
class OrderController {
  constructor(private orderFacade: OrderFacade) {}
  
  async createOrder(orderData: CreateOrderData): Promise<Order> {
    return this.orderFacade.createOrder(orderData);
  }
}

このパターンにより、複雑なビジネスロジックがFacadeに集約され、クライアントコードが簡潔になります。また、並列処理によるパフォーマンス最適化も実現できます。

システム初期化のFacade

アプリケーション起動時の複雑な初期化処理を統合する実装例です。

class ApplicationFacade {
  constructor(
    private configService: ConfigService,
    private databaseService: DatabaseService,
    private cacheService: CacheService,
    private authService: AuthService,
    private loggerService: LoggerService
  ) {}

メインの初期化メソッドで段階的な初期化を実行します。

  async initialize(): Promise<void> {
    await this.loggerService.info('Application initialization started');
    
    try {
      // 段階的初期化(依存順序を考慮)
      await this.initializeConfig();
      await this.initializeInfrastructure();
      await this.initializeServices();
      await this.runHealthChecks();
      
      await this.loggerService.info('Application initialization completed');
      
    } catch (error) {
      await this.loggerService.error('Application initialization failed', error);
      throw error;
    }
  }

個別の初期化処理をそれぞれ実装します。

  private async initializeConfig(): Promise<void> {
    await this.configService.load();
  }
  
  private async initializeInfrastructure(): Promise<void> {
    await Promise.all([
      this.databaseService.connect(),
      this.cacheService.connect()
    ]);
  }
  
  private async initializeServices(): Promise<void> {
    await this.authService.initialize();
  }

ヘルスチェックで全サービスの状態を確認します。

  private async runHealthChecks(): Promise<void> {
    const checks = await Promise.all([
      this.databaseService.healthCheck(),
      this.cacheService.healthCheck(),
      this.authService.healthCheck()
    ]);
    
    if (checks.some(check => !check.healthy)) {
      throw new Error('Health check failed');
    }
  }
}

よくある問題

Facadeが肥大化する

Facadeクラスに多くの責務が集中してしまう問題です。複数のFacadeに分割することで解決できます。

// ❌ 肥大化したFacade
class MegaFacade {
  // ユーザー関連
  async createUser() {}
  async updateUser() {}
  async deleteUser() {}
  
  // 注文関連
  async createOrder() {}
  async cancelOrder() {}
  
  // 商品関連
  async addProduct() {}
  async updateProduct() {}
  
  // 50個以上のメソッド...
}

// ✅ 責務ごとにFacadeを分割
class UserManagementFacade {
  async createUser() {}
  async updateUser() {}
  async deleteUser() {}
}

class OrderManagementFacade {
  async createOrder() {}
  async cancelOrder() {}
}

class ProductManagementFacade {
  async addProduct() {}
  async updateProduct() {}
}

サブシステムの直接アクセス

FacadeとサブシステムのAPIの両方が利用可能になってしまう問題です。アクセス制御により解決できます。

// ✅ サブシステムの直接アクセスを制限
class OrderFacade {
  // privateコンストラクタでサブシステムへの直接アクセスを防ぐ
  constructor(
    private readonly userService: UserService,
    private readonly paymentService: PaymentService
  ) {}
  
  // ファクトリメソッドでFacadeの使用を強制
  static create(): OrderFacade {
    return new OrderFacade(
      new UserService(), 
      new PaymentService()
    );
  }
}

Q: いつFacadeパターンを使うべき?

A: 弊社では複数のサービスを組み合わせる処理、または外部システムとの複雑な連携時に使用します。

Q: ServiceLayerパターンとの違いは?

A: Facadeはインターフェースの簡略化、ServiceLayerはビジネスロジックの組織化が主目的です。

Q: パフォーマンスは大丈夫?

A: 処理を適切に並列化することで、むしろパフォーマンスが向上する場合があります。