Facade Pattern
プロンプトに「Facade」を含めることで、ClaudeCodeは複数のサブシステムへの複雑な処理を単一のインターフェースにまとめる実装を生成できます。弊社では、外部API連携、複数サービスの統合、複雑な初期化処理の簡略化など、システムの複雑さをクライアントから隠したい場合にFacadeパターンを使用します。
弊社の推奨ルール
- 複雑性の隠蔽 - 複数システムの詳細を単一インターフェースで提供
- 依存関係の整理 - クライアントコードの外部依存を最小化
- 高レベルAPIの提供 - よく使われる操作を簡単なメソッドで実装
- エラーハンドリングの統合 - 複数システムのエラーを統一的に処理
- パフォーマンス最適化 - 複数呼び出しをまとめて効率化
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: 処理を適切に並列化することで、むしろパフォーマンスが向上する場合があります。