December 19, 2025 (7mo ago) — last updated July 24, 2026 (2d ago)

アダプタパターン — TypeScript・React・Node.js 実践例

TypeScript、React、Node.js の実例でアダプタパターンを学び、互換性のないインターフェースを統一してレガシーを近代化する手法を紹介します。

← Back to blog
Cover Image for アダプタパターン — TypeScript・React・Node.js 実践例

既存のライブラリやレガシーモジュールが動作しているのに、システムとインターフェースが合わずに困ったことはありませんか。アダプタパターンを使えば、既存コードを変更せずに異なるインターフェースを橋渡しできます。この記事では概念、TypeScript/React の統一例、Node.js のコールバックを Promise に変換するアダプタ例を示し、実践的な設計とテスト方法を解説します。

アダプタパターン: TypeScript、React、Node.js の例

概要: 実用的なコード例を通して、TypeScript、React、Node.js における互換性のないインターフェースをアダプタパターンがどのように接続し、統合やレガシーコードの近代化を助けるかを学びます。

はじめに

既存のライブラリやレガシーモジュールが動作しているのに、システムの他の部分とインターフェースが合わずに困ったことはありませんか。アダプタパターンは、一方のインターフェースを別のインターフェースに変換することで、既存コードを変更せずに再利用を可能にします。この記事では概念の説明に加え、TypeScript と React の実践例、コールバックベースの Node.js モジュールを Promise ベースに変換するアダプタ例を示します。テストしやすく、保守しやすい統合手法に焦点を当てます。

アダプタパターンが重要な理由

アダプタは構造的パターンで、互換性のないオブジェクトをラップしてクライアントが期待するインターフェースを提供します。Gang of Four によって最初に文書化されました1。アダプタはサードパーティ API 統合、レガシーコードの近代化、異なるデータソースの統一に有用です。

アダプタが役立つ一般的なシナリオ:

  • 異なるデータ形状を返すサードパーティ API の統合
  • レガシーなコールバック API を async/await に対応させる近代化
  • UI コンポーネント向けに複数のデータソースを単一のインターフェースに統一する

アダプタを導入するとビジネスロジックは外部システムから切り離され、コードはテストしやすくなります。

パターンの概要

ConceptDescription
TypeStructural
Primary intentAllow objects with incompatible interfaces to work together
Core ideaWrap the adaptee to expose the target interface
Key problem solvedReuse existing classes without changing their source code
Common use casesThird-party libraries, legacy code, multiple data sources

構造と役割

パターンの主要な役割:

  1. クライアント — 特定のインターフェースを必要とするコード
  2. ターゲットインターフェース — クライアントが期待する契約
  3. アダプティー(Adaptee) — 必要な機能を持つが互換性のない既存クラスやモジュール
  4. アダプタ — ターゲットを実装し、呼び出しを翻訳してアダプティーに委譲する

一般的なスタイル:

  • オブジェクトアダプタ(コンポジション): アダプタがアダプティーのインスタンスを保持する。柔軟で推奨される。
  • クラスアダプタ(継承): アダプタがアダプティーを継承する。多重継承が必要になるため、現代の JavaScript/TypeScript ではあまり使われない。

実践例: TypeScript + React

ダッシュボードが 2 つのサービスから異なる形でユーザープロファイルを受け取るとします。アダプタがないとコンポーネントは条件分岐で煩雑になります。

互換性のない API 形状

// Data from UserServiceA
interface UserA {
  userId: number;
  fullName: string;
  emailAddress: string;
}

// Data from UserServiceB
interface UserB {
  id: string;
  name: string;
  contact: {
    email: string;
  };
}

アプリが期待するターゲットインターフェース

interface UnifiedUser {
  id: string;
  name: string;
  email: string;
}

TypeScript のアダプタ

// Adapter for UserServiceA
function adaptUserA(userA: UserA): UnifiedUser {
  return {
    id: userA.userId.toString(),
    name: userA.fullName,
    email: userA.emailAddress,
  };
}

// Adapter for UserServiceB
function adaptUserB(userB: UserB): UnifiedUser {
  return {
    id: userB.id,
    name: userB.name,
    email: userB.contact.email,
  };
}

変換ロジックを中央に集約することで、React コンポーネントは単一で予測可能なデータ形状に依存できます。API のフィールド名が変わっても、修正はアダプタ内だけで済みます。

統一されたデータを消費する React コンポーネント

interface UserProfileProps {
  user: UnifiedUser;
}

const UserProfile: React.FC<UserProfileProps> = ({ user }) => {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>ID: {user.id}</p>
      <p>Email: {user.email}</p>
    </div>
  );
};

このコンポーネントはテストや再利用が容易です。

例: コールバックベースの Node.js モジュールを近代化する

レガシーモジュールはしばしばエラー優先のコールバックを使います。安定したモジュールを変更せずに、Promise ベースの API を公開するアダプタを作ると、呼び出し側は async/await で扱えるようになります。

レガシーなアダプティー(変更しない)

// legacyFileProcessor.js
const fs = require('fs');

class LegacyFileProcessor {
  processFile(filePath, callback) {
    fs.readFile(filePath, 'utf8', (err, data) => {
      if (err) {
        return callback(err, null);
      }
      const processedContent = data.toUpperCase();
      callback(null, processedContent);
    });
  }
}

module.exports = LegacyFileProcessor;

Promise を返すアダプタ

// FileProcessorAdapter.js
const LegacyFileProcessor = require('./legacyFileProcessor');

class FileProcessorAdapter {
  constructor() {
    this.legacyProcessor = new LegacyFileProcessor();
  }

  processFile(filePath) {
    return new Promise((resolve, reject) => {
      this.legacyProcessor.processFile(filePath, (err, data) => {
        if (err) return reject(err);
        resolve(data);
      });
    });
  }
}

module.exports = FileProcessorAdapter;

Node の util.promisify に似た変換ですが、アダプタに適応ロジックを明示的に置くことでテストしやすくなります3

アプリケーションコードでのアダプタの使用

const FileProcessorAdapter = require('./FileProcessorAdapter');
const fileProcessor = new FileProcessorAdapter();

async function handleFileProcessing() {
  try {
    console.log('Processing file with modern async/await...');
    const content = await fileProcessor.processFile('my-file.txt');
    console.log('Processed Content:', content);
  } catch (error) {
    console.error('An error occurred:', error);
  }
}

handleFileProcessing();

これによりレガシーコードを手つかずに保ちながら、コードベースの他部分には近代的なインターフェースを提供できます。

いつアダプタを使うべきか

アダプタはインターフェース不一致を解消したいときに使います。典型的なケース:

  • サードパーティ API の出力がアプリのモデルと一致しないとき
  • レガシーなコールバック API を async/await に対応させたいとき
  • 異なるフォーマットを持つ複数のデータソースを統一したいとき

アダプタを使うべきでないケース:

  • 両方のシステムを自分で制御でき、簡単なリファクタで不一致が解消できる場合は、直接リファクタを優先する
  • サブシステム全体を簡略化したい場合はファサードのほうが適している

判断チェックリスト

SituationUse Adapter?Why
サードパーティライブラリがアプリと不一致Yesライブラリを変えられないため適応が必要
両側を制御できる・小さな変更で解決No直接リファクタのほうが単純
高レベルな単一インターフェースが必要NoFacade が適切
レガシーの段階的移行Yes旧コンポーネントをラップして新しい契約に合わせる
複数の異なるデータソースを統一Yes各ソースに対してアダプタを用意する

テストとパフォーマンス

アダプタはコアロジックを外部システムから切り離すため、テスト容易性が向上します。アダプタのインターフェースをモックすれば、コンポーネント単体のテストが可能で、アダプタ自体も変換ロジックを個別に検証できます。

アダプタによるパフォーマンスのオーバーヘッドは一般に最小限で、関数呼び出しが1回増える程度です。ネットワーク I/O や DB クエリと比較すると無視できることが多く、保守性の向上がわずかなコストを上回ります。JavaScript は最も広く使われている言語の一つであり、統合作業の頻度は高いです4

よくある質問(簡潔)

Q: アダプタパターンはどんな問題を解決するのか?

A: クライアントの呼び出しをアダプティーが理解できる形に翻訳し、インターフェース不一致を解消します。

Q: レガシーコードにどう役立つのか?

A: レガシーモジュールをラップしてモダンな API を提供できるため、リスクの高い書き換えなしに統合できます。

Q: アダプタとファサードの違いは何か?

A: アダプタは互換性の問題解決に特化し、ファサードは複雑なサブシステムを高レベルで簡略化します。

参考文献と内部リンク


At Clean Code Guy, we help teams implement practical design patterns that turn brittle, complex codebases into assets that are resilient, testable, and a pleasure to work on. If you’re wrestling with a legacy system or tricky integrations, our Clean Code Audits can give you a clear, actionable roadmap to a healthier architecture. Learn how we can help you ship better code, faster.

1.
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1994). [https://en.wikipedia.org/wiki/Design_Patterns_(book)](https://en.wikipedia.org/wiki/Design_Patterns_(book))
2.
Adapter pattern overview and examples: https://www.geeksforgeeks.org/adapter-pattern/
3.
Node.js documentation for util.promisify: https://nodejs.org/api/util.html#utilpromisifyoriginal
4.
Stack Overflow Developer Survey 2023 — JavaScript は最も広く使用されている言語の一つで、多くの統合課題が発生していることを示す。 https://survey.stackoverflow.co/2023/
← Back to blog
🙋🏻‍♂️

AIがコードを書きます。
あなたがそれを長持ちさせます。

AI加速の時代において、クリーンコードは単なる良い実践ではありません—スケールするシステムと自らの重みで崩壊するコードベースの違いです。