---
title: "Facadeパターンとは？窓口クラスの実装とLaravel Facadeとの違いを解説"
url: "https://www.issoh.co.jp/tech/details/17608/"
published: 2026-09-15
updated: 2026-09-15
categories: ["アーキテクチャ"]
publisher: "株式会社一創"
---

# Facadeパターンとは？窓口クラスの実装とLaravel Facadeとの違いを解説

注文を1件確定するだけで、在庫の引き当て、決済の実行、配送伝票の発行と、3つのサービスを決まった順序で呼ぶ。この呼び出し手順がコントローラにも管理画面にもバッチにもコピーされていく状態が、Facadeパターンを入れる典型的な合図になります。Facadeは複雑なサブシステム群に対して、単純化された窓口を1つ用意する構造パターンです。この記事では、窓口クラスの実装手順をTypeScriptのコードで示したうえで、LaravelのFacadeがGoFのそれとは別物である根拠を公式ドキュメントと実装ソースから確認し、AdapterやゲートウェイAPIとの境界、レガシー移行での使い方、そして入れてはいけない場面までを扱います。

## まとめ：Facadeを採用する条件と持ち込むべきでない条件を先に示す

Facadeパターンは、複数のクラスやサービスを決まった順序で呼ぶ手順を、1つの窓口クラスに集約する設計です。GoFの23パターンのうち構造に関するパターンに分類され、Adapter・Bridge・Composite・Decorator・Flyweight・Proxyと同じ7種の一員にあたります。全体像は[GoFのデザインパターンとは？23種類の一覧と代表パターンをコードで解説](https://www.issoh.co.jp/tech/details/2615/)にまとめてあるので、分類の位置づけから確認したい場合はそちらを先に読んでください。

採用の判断は3点で足ります。第1に、同じ呼び出し手順が2か所以上に複製されているか。第2に、その手順の中に順序の制約か失敗時の後始末があるか。第3に、呼び出す側がサブシステムの内部を知る必要が本当にあるか。3つ目がノーなら窓口を立てる価値があります。逆に、呼ぶ先が1クラスだけ、順序の制約もない、という状況で窓口を挟むと、ただの転送クラスが1枚増えるだけになります。

持ち込むべきでない場面についても、判断の線引きは明確です。窓口クラスに業務ルールや条件分岐を書き足していく運用に入った時点で、Facadeは神クラスへ向かいます。Laravel公式も、ファサードは書きやすいがゆえにクラスの責務が膨らむ「scope creep」を最大の危険として挙げています。窓口は手順をまとめる場所であって、判断を置く場所ではない、という線引きを最初に決めてから書き始めてください。

## Facadeパターンの構造と役割：窓口クラスがサブシステムを隠す仕組み

構造そのものは単純で、覚えるべき登場人物は2種類しかありません。ただし「何を隠しているのか」を取り違えると、単なる転送クラスを量産することになります。

### Facadeの窓口クラスとサブシステムの責務をクラス図で押さえる

Facadeの登場人物は、窓口となるFacadeクラスと、その裏にある複数のサブシステムクラスだけです。Facadeはサブシステムを知っていますが、サブシステム側はFacadeの存在を知りません。この一方向の依存が成立していることが、後からサブシステムを差し替えられる条件になります。

クラス図で描くと、クライアントからFacadeへ1本、Facadeから各サブシステムへ複数本の関連が伸び、逆向きの矢印は出てきません。矢印が双方向になった時点で設計が壊れているので、実装前に図で確認しておくと事故が減ります。関連や多重度の記法は[クラス図とは？UMLの書き方・関連や多重度・集約とコンポジションの違いを解説](https://www.issoh.co.jp/tech/details/13471/)で扱っています。

### Facadeが隠すのは手順であって機能そのものではない点を確認する

よくある誤解が、Facadeを導入したらサブシステムを直接触ってはいけない、というものです。GoFの定義上、Facadeは高水準の入口を追加するだけで、サブシステムへの直接アクセスを禁止しません。細かい制御が必要な処理は今までどおり個別クラスを直接呼べばよく、定型の8割をFacade経由に寄せる、という使い分けが現実的です。

言い換えると、Facadeが隠すのは機能ではなく呼び出し手順です。どのクラスをどの順で呼び、途中で失敗したら何を巻き戻すか。この知識が呼び出し側に散らばっているのが問題であって、サブシステムの存在自体は隠す対象ではありません。委譲でクラスを組み合わせる考え方そのものは[オブジェクト指向とは？三本柱の実装手順とrecord・sealedまでJavaコードで解説](https://www.issoh.co.jp/tech/details/13412/)の延長線上にあります。

### 標準ライブラリに見るFacade：JdbcTemplateが引き受ける定型処理

自分で書く前に、広く使われている実装を1つ見ておくと粒度の感覚がつかめます。Spring FrameworkのJdbcTemplateは、[公式リファレンスのJDBC Coreの説明](https://docs.spring.io/spring-framework/reference/data-access/jdbc/core.html)によれば、リソースの作成と解放を処理し、SQLクエリの実行、更新やストアドプロシージャ呼び出し、ResultSetの反復と戻り値の抽出、そしてJDBC例外を`org.springframework.dao`の例外階層へ変換する役割を引き受けます。

一方で、アプリケーション側に残るものも明示されています。SQLを与えることと、結果を取り出すこと。この2つは呼び出し側の責務のままです。窓口が引き受ける範囲と、呼び出し側に残す範囲を文書で線引きしている点が参考になります。なお同ドキュメントは、設定後のJdbcTemplateインスタンスはスレッドセーフであり、単一インスタンスを複数のDAOへ共有注入できるとも述べています。

## TypeScriptで注文処理のFacadeを実装して呼び出し手順を1本化する

ここからは、実際に手を動かして実装する段階です。在庫・決済・配送の3サービスを順に呼ぶ注文確定処理を題材に、窓口クラスを1つ立てるまでを追います。以下のコードはそのまま写して動かせる形にしてあります。

### サブシステムの3クラスを定義してから窓口側の引数と戻り値を決める

最初にサブシステムを用意します。ここでの狙いは、各クラスが単体で完結していて、互いを知らない状態を保つことです。決済が在庫を呼ぶ、といった横の依存を作ってしまうと、後で窓口を差し替えられなくなります。

```
// subsystems.ts — 在庫・決済・配送。互いを知らない3クラス
export class InventoryService {
  reserve(sku: string, qty: number): string {
    if (qty <= 0) throw new Error("qty must be positive");
    return `rsv_${sku}_${qty}`;
  }
  release(reservationId: string): void {
    console.log("released:", reservationId);
  }
}

export class PaymentService {
  charge(customerId: string, amount: number): string {
    if (amount > 1000000) throw new Error("amount limit exceeded");
    return `pay_${customerId}_${amount}`;
  }
  refund(paymentId: string): void {
    console.log("refunded:", paymentId);
  }
}

export class ShippingService {
  createLabel(reservationId: string, address: string): string {
    return `lbl_${reservationId}_${address.length}`;
  }
}
```

窓口の引数と戻り値は、サブシステムの都合ではなく呼び出し側の語彙で決めます。`reservationId`や`paymentId`のような内部の識別子を戻り値に並べると、呼び出し側が結局サブシステムを理解する必要が出てきます。戻り値として返すのは、注文番号1つだけで十分です。

### Facadeクラスに呼び出し順序と例外処理をまとめて置き換える

窓口クラスの中身は、順序と後始末の2つです。在庫を押さえてから決済し、決済が失敗したら在庫を解放する。この巻き戻しの知識をここに閉じ込めます。

```
// order-facade.ts — 呼び出し順序と失敗時の巻き戻しを1か所に閉じ込める
import { InventoryService, PaymentService, ShippingService } from "./subsystems";

export class OrderFacade {
  constructor(
    private inventory = new InventoryService(),
    private payment = new PaymentService(),
    private shipping = new ShippingService()
  ) {}

  placeOrder(customerId: string, sku: string, qty: number,
             amount: number, address: string): string {
    const reservationId = this.inventory.reserve(sku, qty);
    let paymentId: string;
    try {
      paymentId = this.payment.charge(customerId, amount);
    } catch (e) {
      this.inventory.release(reservationId);  // 決済失敗なら在庫を戻す
      throw e;
    }
    try {
      const label = this.shipping.createLabel(reservationId, address);
      return `ord_${label}`;
    } catch (e) {
      this.payment.refund(paymentId);
      this.inventory.release(reservationId);
      throw e;
    }
  }
}
```

コンストラクタでサブシステムを受け取る形にしておくと、テストではモックを差し込めます。窓口クラスを新規メソッドの置き場にしないため、公開メソッドは業務上の操作名だけに絞ってください。`getInventoryService()`のような内部を漏らすメソッドを生やした瞬間に、窓口の意味はなくなります。

### クライアント側のコードがどれだけ短くなるかを実行結果で確かめる

導入前後の差は、呼び出し側の行数ではなく「知っている前提の数」で測ります。導入前のコードは3クラスの存在と呼び順と巻き戻しを知っていました。導入後に知っているのは`OrderFacade`1つだけです。

```
// client.ts — 窓口ができた後の呼び出し側
import { OrderFacade } from "./order-facade";

const facade = new OrderFacade();
const orderId = facade.placeOrder("cus_001", "SKU-42", 2, 12800, "Tokyo");
console.log(orderId);
// 実行例: npx tsx client.ts
// => ord_lbl_rsv_SKU-42_2_5
```

この状態なら、決済サービスを別の事業者へ差し替えても修正はFacadeの内側で閉じます。呼び出し側のコードは1行も変わりません。逆に、差し替え時に呼び出し側まで修正が波及するなら、窓口の抽象が漏れている合図になります。

## LaravelのFacadeがGoFのFacadeと別物である理由を実装で確かめる

「facade」で検索すると、GoFのパターン解説とLaravelのFacade解説が混ざって出てきます。名前は同じですが解いている問題が違うので、ここを分けておかないと設計の議論がかみ合いません。

### Laravel公式が自らstatic proxyと書いている位置づけを読む

[Laravel公式ドキュメントのFacades](https://laravel.com/framework/docs/13.x/facades)は、ファサードをサービスコンテナ内のクラスへの「static proxy」と説明しています。狙いは、長いクラス名を注入せずに簡潔な記法で機能へ到達しつつ、従来の静的メソッドより高いテスト容易性を保つことです。つまり主眼は、複数サブシステムの手順を束ねることではなく、コンテナ解決を短く書くことにあります。

公式は欠点も同じページで述べています。ファサードは注入を必要としないため、1つのクラスが多数のファサードを呼び込んで肥大化する「scope creep」を招きやすい、という指摘です。DIであればコンストラクタが長くなることで肥大に気づけるが、ファサードにはその視覚的な合図がない、とも書かれています。なお2026年9月時点で公式が最新として提示するのは13.x系で、12.x系のページには旧版である旨の警告が表示されます。

### \_\_callStaticでコンテナのインスタンスへ委譲する実装を追う

この仕組みを集約しているのは、1枚の基底クラスです。[laravel/frameworkのFacade.php](https://github.com/laravel/framework/blob/13.x/src/Illuminate/Support/Facades/Facade.php)を読むと、`__callStatic()`が`getFacadeRoot()`でインスタンスを取得し、そのインスタンスに対して同名メソッドを呼び直しているだけだとわかります。`getFacadeRoot()`は`resolveFacadeInstance()`を経由してコンテナから解決し、解決済みインスタンスをキャッシュします。

この`__callStatic()`はPHPの言語機能で、[PHPマニュアルのオーバーロード](https://www.php.net/manual/ja/language.oop5.overloading.php)によれば、アクセスできないメソッドを静的コンテキストで呼び出したときに起動します。`Cache::get()`という記述に`get`という静的メソッドの実体はなく、呼び出しが動的に転送されている、という事実の裏づけとなる説明です。実装を読めば、`getFacadeAccessor`を持たないファサードに対して`RuntimeException('Facade does not implement getFacadeAccessor method.')`を投げることも確認できます。

### カスタムFacadeをgetFacadeAccessorとbindで作る手順

自作する場合の最小構成は、コンテナへのバインドと、バインド名を返すファサードクラスの2つだけです。GoFのFacadeを自作するのとは手順がまったく違う点を、コードで見比べてください。

```
// app/Facades/Report.php — バインド名を返すだけのクラス
namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class Report extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return 'report';  // コンテナのバインディング名
    }
}

// app/Providers/AppServiceProvider.php — コンテナへ実体を登録
public function register(): void
{
    $this->app->singleton('report', function ($app) {
        return new \App\Services\ReportService();
    });
}

// 呼び出し側とテスト
use App\Facades\Report;

$pdf = Report::monthly(2026, 9);

Report::shouldReceive('monthly')->once()->andReturn('dummy.pdf');
```

最後の1行が示しているのは、静的記法のまま期待値を設定できる仕組みです。公式が「テスト容易性を保つ」と述べている根拠がこれで、基底クラスは`swap`や`spy`、`partialMock`といった差し替え用のメソッドを備えています。さらにクラス名の頭に`Facades\`を付けて呼ぶリアルタイムファサードを使えば、任意のクラスを同じ記法で扱えます。

## AdapterやMediatorとの違いとゲートウェイ集約を使い分ける基準

構造パターンは見た目が似ているため、実装後に「これはFacadeなのかAdapterなのか」と揉めがちです。判定は目的と対象数の2軸で決まります。

### 1つのクラスを変換するAdapterと複数を束ねるFacadeの境界

Adapterの目的は変換で、対象は基本的に1つのクラスです。呼び出し側が期待するインターフェースに、既存クラスの形を合わせます。Facadeの目的は単純化で、対象は複数のクラスにまたがる手順です。窓口の裏に呼び先が1つしかないなら、それはAdapterかProxyであってFacadeではありません。

Mediatorとの違いは、依存の向きで見ます。Mediatorは複数オブジェクトの相互通信を仲介役に集めるもので、各オブジェクトは仲介役を知っています。Facadeの裏側にいるサブシステムは窓口の存在を知りません。双方向の通信が必要になった時点で、選ぶべきパターンはFacadeではなくなります。

### マイクロサービスのゲートウェイ集約はFacadeの分散版と読む

クラス内の話をネットワーク越しに引き伸ばすと、APIゲートウェイの設計になります。Azure Architecture Centerの[ゲートウェイ集約パターン](https://learn.microsoft.com/ja-jp/azure/architecture/patterns/gateway-aggregation)は、クライアントが複数のバックエンドへ個別に投げていた要求を、ゲートウェイが1つの要求として受け取り、結果をまとめて返す設計として文書化されています。窓口が手順を引き受ける構図はFacadeと同じです。

ただし分散環境では、導入に伴う代償が増える点に注意が必要です。同ドキュメントは、ゲートウェイが単一障害点になりうること、負荷のボトルネックになりうること、集約処理をゲートウェイ本体に埋め込まず背後の専用サービスへ寄せる選択肢があることを挙げています。プロセス内のFacadeと同じ気軽さで導入すると、可用性の設計をやり直す羽目になります。

## レガシー資産をFacadeで包んで段階的に置き換えるときの進め方

受託開発で窓口クラスが最も効くのは、新規開発よりも既存システムの置き換えです。全面刷新を避けて機能単位で移していく場面で、境界の置き方がそのまま工期に響きます。

### 腐敗防止レイヤーとしてFacadeを置き旧APIの型を閉じ込める

Azure Architecture Centerの[腐敗防止レイヤーパターン](https://learn.microsoft.com/ja-jp/azure/architecture/patterns/anti-corruption-layer)は、意味体系を共有しない2つのサブシステムの間に「ファサードまたはアダプターレイヤー」を実装する設計として定義され、初出はEric EvansのDDDであると明記しています。旧システムの癖のあるデータ構造や古いAPIを、新システム側の語彙へ翻訳する層を置く、という考え方です。

実装上の肝は、この層に翻訳だけを置くことです。同ドキュメントも、ビジネスルールやオーケストレーションをこの層へ入れないよう釘を刺しています。考慮点として挙がるのは、呼び出しに待機時間が加わること、保守対象のサービスが1つ増えること、そして移行完了後に廃止するか恒久的に残すかを先に決めておくことの3点です。ドメインの境界設計そのものは[ドメイン駆動設計（DDD）とは？意味・基本用語・実装と採用判断をわかりやすく解説](https://www.issoh.co.jp/tech/details/3580/)で扱っています。

### 置き換えの途中で内部実装だけを差し替えて呼び出し側を止めずに進める

段階移行の実務手順は3段です。第1段で、旧システムを呼ぶコードを窓口クラス1本に集約し、呼び出し側の修正をここで終わらせます。第2段で、窓口の内側だけを新実装へ向け替える。第3段で、旧システムへの参照が窓口の中だけに残っていることを確認して撤去します。

この順序を守ると、切り替えの影響範囲が窓口クラスのテストに収まります。外部との境界だけを決めて内部を交換可能にする発想は、[ヘキサゴナルアーキテクチャとは？ポートアンドアダプターの仕組みとクリーン・オニオンとの違い](https://www.issoh.co.jp/tech/details/2965/)のポートとアダプターと同じ系譜にあります。窓口を先に立てずに個別呼び出しを直接書き換えると、切り戻しの単位が作れません。

## Facadeが神クラス化する兆候と分割へ踏み切るときの判断基準

Facadeの失敗はほぼ1種類で、窓口が太りすぎることです。兆候は数値で見えるので、レビュー時の観測点を決めておけば早い段階で止められます。

### 窓口クラスの引数が増え続けたら責務が混ざった合図として分割する

分割の合図は3つあります。第1に、公開メソッドの引数が7個を超え、任意引数やフラグが混ざり始めたとき。第2に、窓口の中にif文が現れ、条件で呼び先を変え始めたとき。第3に、業務的に無関係な操作が同じクラスに同居し始めたときです。

対処は単純で、業務の単位で窓口を割ります。注文と返品を1つの窓口に押し込むのをやめ、それぞれに窓口を立てる。窓口同士が相互に呼び合う構造になったら、その時点でFacadeの適用範囲を超えているので、上位のサービス層を設計し直してください。Laravel公式がscope creepへの対処として挙げているのも、クラスが大きくなったら複数の小さなクラスへ分割せよ、という同じ処方です。

### Facadeを採用してよい条件と持ち込むべきでない場面を線引きする

採用してよいのは、同じ呼び出し手順が2か所以上に複製されていて、その手順に順序か後始末の制約がある場合です。外部サービス連携、レガシー置き換え、複数マイクロサービスの集約という3用途では、窓口を立てる費用をほぼ確実に回収できます。社外の相手と接続する境界がある案件では、翻訳層を兼ねさせると効果がさらに明確になります。

見送るべきなのは、呼び先が1クラスだけの場合、手順に順序の制約がない場合、そして呼び出し側が1か所しかない場合です。この3条件のどれかに当てはまるなら、窓口を作らず直接呼ぶほうが読みやすく保てます。判断に迷う規模の連携や、既存システムとの接続を含む設計であれば、[API開発・システム連携](https://www.issoh.co.jp/service/system/api/)で扱っている境界設計の進め方が参考になります。

## よくある質問

### FacadeパターンとAdapterパターンの違いは何ですか？

目的と対象数が違います。Adapterは既存の1クラスのインターフェースを呼び出し側が期待する形へ変換するもので、Facadeは複数クラスにまたがる呼び出し手順を1つの窓口へ単純化するものです。窓口の裏に呼び先が1つしかないなら、それはAdapterかProxyだと考えてください。

### LaravelのFacadeはGoFのFacadeパターンですか？

別物です。Laravel公式はファサードをサービスコンテナ内のクラスへのstatic proxyと説明しており、複数サブシステムの手順を束ねる構造パターンとしては扱っていません。実装も`__callStatic()`による委譲1本で、GoFのFacadeのような呼び出し順序の集約は行いません。

### Facadeを導入するとテストは書きにくくなりますか？

コンストラクタでサブシステムを受け取る形にしておけば、モックを差し込めるので書きにくくはなりません。むしろ呼び出し順序の検証を窓口クラスのテスト1か所へ集約できます。Laravelのファサードについても、公式が`shouldReceive`や`swap`による差し替え手段を用意しています。

### Facadeクラスはいくつまで作ってよいですか？

数の上限より、業務の単位と一致しているかで判断します。注文・返品・在庫調整のように業務が分かれるなら窓口も分けるほうが安全です。窓口同士が相互に呼び合い始めたら、数が多いのではなく分割の軸が間違っています。

### ファサードという語はなぜ建築用語から来ているのですか？

facadeはフランス語由来で建物の正面を指す語です。内部の複雑な構造を見せずに、入口だけを来訪者へ示す建物の正面と、サブシステムを隠して窓口だけを見せる設計が重なるため、この名前が付いています。検索結果に建築分野の解説が混ざるのもこの語源によるものです。

## 関連記事

- [GoFのデザインパターンとは？23種類の一覧と代表パターンをコードで解説](https://www.issoh.co.jp/tech/details/2615/)：Facadeを含む23種の分類と位置づけを一覧で確認できます。
- [クラス図とは？UMLの書き方・関連や多重度・集約とコンポジションの違いを解説](https://www.issoh.co.jp/tech/details/13471/)：窓口とサブシステムの依存方向を図で確認する際の記法です。
- [ヘキサゴナルアーキテクチャとは？ポートアンドアダプターの仕組みとクリーン・オニオンとの違い](https://www.issoh.co.jp/tech/details/2965/)：境界を決めて内部を交換可能にする設計の全体像を扱います。
- [ドメイン駆動設計（DDD）とは？意味・基本用語・実装と採用判断をわかりやすく解説](https://www.issoh.co.jp/tech/details/3580/)：腐敗防止レイヤーの出典となった設計手法です。
- [Rubyのデザインパターン｜Interactor・Factory・DecoratorのRails実装](https://www.issoh.co.jp/tech/details/2880/)：フレームワーク上でパターンを実装する具体例です。

---

出典: [Facadeパターンとは？窓口クラスの実装とLaravel Facadeとの違いを解説](<https://www.issoh.co.jp/tech/details/17608/>)（株式会社一創）
