Observable builds front-end anti-corrosion strategy
1 ジレンマと問題点
フロントエンドが直面する課題をより明確に説明するため、To B ビジネスで一般的なダッシュボードページを例に挙げます。このページには、使用可能なメモリ、使用済みメモリ、使用済みメモリ比率の 3 つの情報が表示されます。
インターフェイスのレスポンス構造が調整されると、MemoryFree コンポーネントのインターフェイス呼び出し方法も調整する必要があります。同様に、MemoryUsage と MemoryUsagePercent も動作するように修正しなければなりません。
実際の To B ビジネスでは数百ものインターフェイスが存在し、コンポーネントとインターフェイスの統合ロジックは上記の例よりもはるかに複雑です。
数年、あるいはそれ以上の反復を経て、インターフェイスには徐々に複数のバージョンが生まれます。インターフェイスの安定性とユーザーの操作性を考慮し、フロントエンドは複数バージョンのインターフェイスに依存して画面を構築することが一般的です。一部のインターフェイスをオフライン化または変更する必要がある場合、フロントエンドはビジネスロジックを再理解し、大量のコード修正を行って画面の安定稼働を確保する必要があります。
フロントエンドに影響を与える一般的なインターフェイス変更には、以下のようなものがあります。
* リターンフィールドの調整
* 呼び出し方法の変更
* 複数バージョンの共存
プラットフォーム型のビジネスに直面すると、こうした問題はさらに困難になります。プラットフォームプロダクトは 1 つ以上の基盤エンジンをカプセル化します。たとえば、機械学習プラットフォームは TensorFlow や PyTorch などの機械学習エンジンに基づいて構築され、リアルタイムコンピューティングプラットフォームは Flink や Spark などのコンピューティングエンジンに基づいて構築される場合があります。
プラットフォームは上位レイヤーでエンジンのインターフェイスの大部分をカプセル化しますが、一部の低レイヤーインターフェイスが直接フロントエンドに透過的に渡されることは避けられず、インターフェイス変更による課題が生じます。
フロントエンドが直面するジレンマは、フロントエンドとバックエンドの独特な関係性に起因します。他の分野とは異なり、To B ビジネスではフロントエンドが通常バックエンドサプライヤーからの供給を受ける下流の顧客として機能し、場合によってはバックエンドのフォロワーになることもあります。
顧客/供給者関係では、フロントエンドは下流に位置し、バックエンドチームは上流に位置します。インターフェイスの内容とリリース時期は通常バックエンドチームが決定します。
フォロワー関係では、上流のバックエンドチームはフロントエンドチームの要求に応じて調整を行うことはなく、フロントエンドは上流バックエンドのモデルに従うしかありません。この状況は通常、フロントエンドが上流バックエンドチームに影響を与えられない場合に発生します。たとえば、フロントエンドがオープンソースプロジェクトに基づいてインターフェイスを設計する必要がある場合や、バックエンドチームのモデルが非常に成熟していて変更が難しい場合などです。
『Clean Architecture』の著者は、このような埋め込みアーキテクチャ設計の困難な問題について記述しており、これは前述のジレンマとよく似ています。
ソフトウェアは本来長期間使用されるものであり、ハードウェアが進化するにつれてファームウェアは時代遅れになりますが、実際にはソフトウェア自体は時間とともに劣化するわけではありません。しかしハードウェアとそのファームウェアは時間とともに時代遅れとなり、ソフトウェアにも対応する変更が必要になります。
顧客/供給者関係でもフォロワー関係でも、ソフトウェアがハードウェアの開発と反復を決定できないのと同様に、フロントエンドもエンジンとインターフェイスの設計を決定することは困難、あるいは不可能です。フロントエンド自体は時間とともに使用不能になることはありませんが、技術エンジンと関連インターフェイスは時間とともに時代遅れとなり、フロントエンドコードは技術エンジンの反復と置換に従って徐々に腐敗し、最終的に書き直しを余儀なくされる運命から逃れられません。
2 アンチ腐食層の設計
Windows が誕生するずっと前から、エンジニアは前述のハードウェア、ファームウェア、ソフトウェアの保守性の問題を解決するために、HAL(ハードウェアアブストラクションレイヤー)の概念を導入していました。HAL はソフトウェアにサービスを提供し、ハードウェアの実装の詳細を隠蔽することで、ソフトウェアがハードウェアやファームウェアの変更に伴う頻繁な修正を不要にします。
HAL の設計思想は、ドメイン駆動設計(DDD)ではアンチ腐食層とも呼ばれています。DDD が定義するさまざまなコンテキストマッピング関係の中で、アンチ腐食層は最も防御的なものです。下流チームが外部の技術的嗜好やドメインモデルの侵入を防ぐ必要がある場合によく使用され、上流モデルと下流モデルの隔離に役立ちます。
フロントエンドにアンチ腐食層の概念を導入することで、現在のバックエンドのコンテキストマッピングインターフェイス変更がフロントエンドコードに与える影響を軽減または回避できます。
業界ではアンチ腐食層の実装方法が多数存在します。近年人気を集めている GraphQL や BFF も代替手段として使用できますが、技術選定はビジネスシナリオに制約されます。To C ビジネスとは異なり、To B ビジネスではフロントエンドとバックエンドの関係は通常、顧客/供給者またはフォロワー/フォロワー関係です。この関係性の中で、バックエンドがフロントエンドに協力して GraphQL でインターフェイスを変換することを期待するのは非現実的であり、BFF の構築には通常、追加のデプロイリソースと運用コストが必要です。
上記の状況では、ブラウザ側でアンチ腐食層を構築する方が実現可能なソリューションですが、ブラウザでのアンチ腐食層構築にも課題があります。
React、Angular、Vue を問わず、データ層のソリューションは数多く存在します。Mobx、Redux、Vuex などがありますが、これらのデータ層ソリューションは実際にはビュー層に侵入します。ビュー層と完全にデカップリングできるアンチ腐食層のソリューションはあるでしょうか。RxJS に代表される Observable ソリューションが、現時点で最適な選択肢かもしれません。
RxJS は ReactiveX プロジェクトの JavaScript 実装で、もともとは Microsoft のアーキテクト Erik Meijer が率いるチームが開発した LINQ の拡張です。このプロジェクトの目標は、開発者が非同期データストリームをより便利に処理できるよう、一貫したプログラミングインターフェイスを提供することです。現在、RxJS はリアクティブプログラミング開発ツールとしてよく使用されていますが、アンチ腐食層を構築するシナリオでも、RxJS に代表される Observable ソリューションは大きな力を発揮します。
RxJS を選んだ主な理由は以下の通りです。
* 異なるデータソースを統一する能力:RxJS は WebSocket、HTTP リクエスト、ユーザー操作、ページクリックなどを統一された Observable オブジェクトに変換できます。
* 異なるタイプのデータを統一する能力:RxJS は非同期データと同期データを Observable オブジェクトに統一します。
* 豊富なデータ処理能力:RxJS は豊富なオペレーターを提供し、サブスクリプション前に Observable の事前処理が可能です。
フロントエンドアーキテクチャに侵入しない:RxJS の Observable と Promise は相互に変換できるため、RxJS のすべての概念をデータ層に完全にカプセル化し、ビュー層には Promise のみを公開できます。
RxJS を導入してすべてのタイプのインターフェイスを Observable オブジェクトに変換すると、フロントエンドのビューコンポーネントは Observable にのみ依存し、インターフェイス実装の詳細からデカップリングされます。同時に、Observable は Promise に変換できるため、ビュー層で取得した Promise は任意のデータ層ソリューションやフレームワークと組み合わせて使用できます。
Promise への変換に加えて、レンダリング層で rxjs-hooks などの RxJS ソリューションと組み合わせて使用することもでき、より良い開発体験が得られます。
3 アンチ腐食層の実装
前述のアンチ腐食層設計を参照し、ダッシュボードプロジェクトの初期段階で RxJS Observable をコアとしたアンチ腐食層コードを実装します。
MemoryUsagePercent の実装コードは以下の通りです。この時点でコンポーネントは特定のインターフェイスに依存せず、アンチ腐食層の実装に直接依存します。
Observable アンチ腐食層には、高レベル Observable と低レベル Observable の 2 つの設計があります。上記の例では、Free Observable と Usage Observable が低レベルパッケージで、Percent Observable は Free Observable と Usage Observable を使用する高レベルのカプセル化です。低レベルのカプセル化が変更されても、Observable 自体の特性により、高レベルのカプセル化は通常変更を必要としません。これはアンチ腐食層がもたらす追加のメリットです。
2 呼び出し方法の変更
呼び出し方法が変更された場合も、アンチ腐食層は効果を発揮します。/api/v3/memory は free と usage のデータを直接返し、インターフェイス形式は以下の通りです。
アンチ腐食層のコードは以下のように更新するだけで、コンポーネント層のコードを修正せずに済みます。
3 複数バージョンの共存と使用
フロントエンドコードを複数の環境にデプロイする必要がある場合、一部の環境では v3 インターフェイスが利用可能で、一部の環境では v2 インターフェイスのみがデプロイされています。この場合も、アンチ腐食層で環境の差異を遮断できます。
race オペレーターを使用することで、v2 または v3 のいずれかのバージョンのインターフェイスが利用可能であれば、アンチ腐食層は正常に機能し、コンポーネント層へのインターフェイスの影響に注意を払う必要はありません。
4 その他の応用
アンチ腐食層は単なるインターフェイスの追加カプセル化と隔離ではなく、以下のような役割も果たします。
1 概念マッピング
インターフェイスのセマンティクスとフロントエンドが必要とするデータのセマンティクスは、完全に対応しない場合があります。コンポーネント層で直接インターフェイスを呼び出す場合、すべての開発者がインターフェイス間のセマンティックマッピングを十分に理解する必要があります。アンチ腐食層があれば、アンチ腐食層が提供する呼び出し方法にデータの真のセマンティクスが含まれているため、開発者の二次理解のコストを削減できます。
2 形式適応
多くの場合、インターフェイスが返すデータ構造と形式は、フロントエンドが必要とするデータ形式と一致しません。アンチ腐食層でデータ変換ロジックを追加することで、インターフェイスデータがビジネスコードに与える影響を軽減できます。上記のケースでは、getMemoryUsagePercent の戻りデータをカプセル化し、コンポーネント層が変換なしでパーセンテージデータを直接使用できるようにしています。
3 インターフェイスキャッシュ
複数のビジネスが同じインターフェイスに依存している場合、アンチ腐食層でキャッシュロジックを追加することで、インターフェイス呼び出しの負荷を効果的に削減できます。
形式適応と同様に、キャッシュロジックをアンチ腐食層にカプセル化することで、コンポーネント層でのデータの二次キャッシュを回避し、キャッシュされたデータを一元管理してコードの複雑さを軽減できます。シンプルなキャッシュの例は以下の通りです。
4 安定性のポケット
インターフェイスの安定性が低い場合、通常の対応はコンポーネント層でレスポンスエラーを処理することです。このやり取りのロジックは通常複雑で、コンポーネント層のメンテナンスコストが高くなります。アンチ腐食層で安定性をチェックし、インターフェイスが失敗した場合はフォールバックビジネスデータを返すことができます。フォールバックデータはアンチ腐食層で一元管理されるため、テストと修正がより便利になります。前述の複数バージョン共存のアンチ腐食層に以下のコードを追加することで、v2 と v3 のインターフェイスがデータを返せない場合でも、フロントエンドは利用可能な状態を維持できます。
5 共同デバッグとテスト
インターフェイスとフロントエンドが並行開発されている状態があるかもしれません。この時、フロントエンドの開発には実際のバックエンドインターフェイスが利用できません。従来のモック API を構築する方法と比較して、アンチ腐食層で直接データをモックする方がより簡便なソリューションです。
アンチ腐食層でのデータモックはページのテストにも使用できます。たとえば、大量のデータをモックしてページパフォーマンスへの影響をテストできます。
フロントエンドが直面する課題をより明確に説明するため、To B ビジネスで一般的なダッシュボードページを例に挙げます。このページには、使用可能なメモリ、使用済みメモリ、使用済みメモリ比率の 3 つの情報が表示されます。
インターフェイスのレスポンス構造が調整されると、MemoryFree コンポーネントのインターフェイス呼び出し方法も調整する必要があります。同様に、MemoryUsage と MemoryUsagePercent も動作するように修正しなければなりません。
実際の To B ビジネスでは数百ものインターフェイスが存在し、コンポーネントとインターフェイスの統合ロジックは上記の例よりもはるかに複雑です。
数年、あるいはそれ以上の反復を経て、インターフェイスには徐々に複数のバージョンが生まれます。インターフェイスの安定性とユーザーの操作性を考慮し、フロントエンドは複数バージョンのインターフェイスに依存して画面を構築することが一般的です。一部のインターフェイスをオフライン化または変更する必要がある場合、フロントエンドはビジネスロジックを再理解し、大量のコード修正を行って画面の安定稼働を確保する必要があります。
フロントエンドに影響を与える一般的なインターフェイス変更には、以下のようなものがあります。
* リターンフィールドの調整
* 呼び出し方法の変更
* 複数バージョンの共存
プラットフォーム型のビジネスに直面すると、こうした問題はさらに困難になります。プラットフォームプロダクトは 1 つ以上の基盤エンジンをカプセル化します。たとえば、機械学習プラットフォームは TensorFlow や PyTorch などの機械学習エンジンに基づいて構築され、リアルタイムコンピューティングプラットフォームは Flink や Spark などのコンピューティングエンジンに基づいて構築される場合があります。
プラットフォームは上位レイヤーでエンジンのインターフェイスの大部分をカプセル化しますが、一部の低レイヤーインターフェイスが直接フロントエンドに透過的に渡されることは避けられず、インターフェイス変更による課題が生じます。
フロントエンドが直面するジレンマは、フロントエンドとバックエンドの独特な関係性に起因します。他の分野とは異なり、To B ビジネスではフロントエンドが通常バックエンドサプライヤーからの供給を受ける下流の顧客として機能し、場合によってはバックエンドのフォロワーになることもあります。
顧客/供給者関係では、フロントエンドは下流に位置し、バックエンドチームは上流に位置します。インターフェイスの内容とリリース時期は通常バックエンドチームが決定します。
フォロワー関係では、上流のバックエンドチームはフロントエンドチームの要求に応じて調整を行うことはなく、フロントエンドは上流バックエンドのモデルに従うしかありません。この状況は通常、フロントエンドが上流バックエンドチームに影響を与えられない場合に発生します。たとえば、フロントエンドがオープンソースプロジェクトに基づいてインターフェイスを設計する必要がある場合や、バックエンドチームのモデルが非常に成熟していて変更が難しい場合などです。
『Clean Architecture』の著者は、このような埋め込みアーキテクチャ設計の困難な問題について記述しており、これは前述のジレンマとよく似ています。
ソフトウェアは本来長期間使用されるものであり、ハードウェアが進化するにつれてファームウェアは時代遅れになりますが、実際にはソフトウェア自体は時間とともに劣化するわけではありません。しかしハードウェアとそのファームウェアは時間とともに時代遅れとなり、ソフトウェアにも対応する変更が必要になります。
顧客/供給者関係でもフォロワー関係でも、ソフトウェアがハードウェアの開発と反復を決定できないのと同様に、フロントエンドもエンジンとインターフェイスの設計を決定することは困難、あるいは不可能です。フロントエンド自体は時間とともに使用不能になることはありませんが、技術エンジンと関連インターフェイスは時間とともに時代遅れとなり、フロントエンドコードは技術エンジンの反復と置換に従って徐々に腐敗し、最終的に書き直しを余儀なくされる運命から逃れられません。
2 アンチ腐食層の設計
Windows が誕生するずっと前から、エンジニアは前述のハードウェア、ファームウェア、ソフトウェアの保守性の問題を解決するために、HAL(ハードウェアアブストラクションレイヤー)の概念を導入していました。HAL はソフトウェアにサービスを提供し、ハードウェアの実装の詳細を隠蔽することで、ソフトウェアがハードウェアやファームウェアの変更に伴う頻繁な修正を不要にします。
HAL の設計思想は、ドメイン駆動設計(DDD)ではアンチ腐食層とも呼ばれています。DDD が定義するさまざまなコンテキストマッピング関係の中で、アンチ腐食層は最も防御的なものです。下流チームが外部の技術的嗜好やドメインモデルの侵入を防ぐ必要がある場合によく使用され、上流モデルと下流モデルの隔離に役立ちます。
フロントエンドにアンチ腐食層の概念を導入することで、現在のバックエンドのコンテキストマッピングインターフェイス変更がフロントエンドコードに与える影響を軽減または回避できます。
業界ではアンチ腐食層の実装方法が多数存在します。近年人気を集めている GraphQL や BFF も代替手段として使用できますが、技術選定はビジネスシナリオに制約されます。To C ビジネスとは異なり、To B ビジネスではフロントエンドとバックエンドの関係は通常、顧客/供給者またはフォロワー/フォロワー関係です。この関係性の中で、バックエンドがフロントエンドに協力して GraphQL でインターフェイスを変換することを期待するのは非現実的であり、BFF の構築には通常、追加のデプロイリソースと運用コストが必要です。
上記の状況では、ブラウザ側でアンチ腐食層を構築する方が実現可能なソリューションですが、ブラウザでのアンチ腐食層構築にも課題があります。
React、Angular、Vue を問わず、データ層のソリューションは数多く存在します。Mobx、Redux、Vuex などがありますが、これらのデータ層ソリューションは実際にはビュー層に侵入します。ビュー層と完全にデカップリングできるアンチ腐食層のソリューションはあるでしょうか。RxJS に代表される Observable ソリューションが、現時点で最適な選択肢かもしれません。
RxJS は ReactiveX プロジェクトの JavaScript 実装で、もともとは Microsoft のアーキテクト Erik Meijer が率いるチームが開発した LINQ の拡張です。このプロジェクトの目標は、開発者が非同期データストリームをより便利に処理できるよう、一貫したプログラミングインターフェイスを提供することです。現在、RxJS はリアクティブプログラミング開発ツールとしてよく使用されていますが、アンチ腐食層を構築するシナリオでも、RxJS に代表される Observable ソリューションは大きな力を発揮します。
RxJS を選んだ主な理由は以下の通りです。
* 異なるデータソースを統一する能力:RxJS は WebSocket、HTTP リクエスト、ユーザー操作、ページクリックなどを統一された Observable オブジェクトに変換できます。
* 異なるタイプのデータを統一する能力:RxJS は非同期データと同期データを Observable オブジェクトに統一します。
* 豊富なデータ処理能力:RxJS は豊富なオペレーターを提供し、サブスクリプション前に Observable の事前処理が可能です。
フロントエンドアーキテクチャに侵入しない:RxJS の Observable と Promise は相互に変換できるため、RxJS のすべての概念をデータ層に完全にカプセル化し、ビュー層には Promise のみを公開できます。
RxJS を導入してすべてのタイプのインターフェイスを Observable オブジェクトに変換すると、フロントエンドのビューコンポーネントは Observable にのみ依存し、インターフェイス実装の詳細からデカップリングされます。同時に、Observable は Promise に変換できるため、ビュー層で取得した Promise は任意のデータ層ソリューションやフレームワークと組み合わせて使用できます。
Promise への変換に加えて、レンダリング層で rxjs-hooks などの RxJS ソリューションと組み合わせて使用することもでき、より良い開発体験が得られます。
3 アンチ腐食層の実装
前述のアンチ腐食層設計を参照し、ダッシュボードプロジェクトの初期段階で RxJS Observable をコアとしたアンチ腐食層コードを実装します。
MemoryUsagePercent の実装コードは以下の通りです。この時点でコンポーネントは特定のインターフェイスに依存せず、アンチ腐食層の実装に直接依存します。
Observable アンチ腐食層には、高レベル Observable と低レベル Observable の 2 つの設計があります。上記の例では、Free Observable と Usage Observable が低レベルパッケージで、Percent Observable は Free Observable と Usage Observable を使用する高レベルのカプセル化です。低レベルのカプセル化が変更されても、Observable 自体の特性により、高レベルのカプセル化は通常変更を必要としません。これはアンチ腐食層がもたらす追加のメリットです。
2 呼び出し方法の変更
呼び出し方法が変更された場合も、アンチ腐食層は効果を発揮します。/api/v3/memory は free と usage のデータを直接返し、インターフェイス形式は以下の通りです。
アンチ腐食層のコードは以下のように更新するだけで、コンポーネント層のコードを修正せずに済みます。
3 複数バージョンの共存と使用
フロントエンドコードを複数の環境にデプロイする必要がある場合、一部の環境では v3 インターフェイスが利用可能で、一部の環境では v2 インターフェイスのみがデプロイされています。この場合も、アンチ腐食層で環境の差異を遮断できます。
race オペレーターを使用することで、v2 または v3 のいずれかのバージョンのインターフェイスが利用可能であれば、アンチ腐食層は正常に機能し、コンポーネント層へのインターフェイスの影響に注意を払う必要はありません。
4 その他の応用
アンチ腐食層は単なるインターフェイスの追加カプセル化と隔離ではなく、以下のような役割も果たします。
1 概念マッピング
インターフェイスのセマンティクスとフロントエンドが必要とするデータのセマンティクスは、完全に対応しない場合があります。コンポーネント層で直接インターフェイスを呼び出す場合、すべての開発者がインターフェイス間のセマンティックマッピングを十分に理解する必要があります。アンチ腐食層があれば、アンチ腐食層が提供する呼び出し方法にデータの真のセマンティクスが含まれているため、開発者の二次理解のコストを削減できます。
2 形式適応
多くの場合、インターフェイスが返すデータ構造と形式は、フロントエンドが必要とするデータ形式と一致しません。アンチ腐食層でデータ変換ロジックを追加することで、インターフェイスデータがビジネスコードに与える影響を軽減できます。上記のケースでは、getMemoryUsagePercent の戻りデータをカプセル化し、コンポーネント層が変換なしでパーセンテージデータを直接使用できるようにしています。
3 インターフェイスキャッシュ
複数のビジネスが同じインターフェイスに依存している場合、アンチ腐食層でキャッシュロジックを追加することで、インターフェイス呼び出しの負荷を効果的に削減できます。
形式適応と同様に、キャッシュロジックをアンチ腐食層にカプセル化することで、コンポーネント層でのデータの二次キャッシュを回避し、キャッシュされたデータを一元管理してコードの複雑さを軽減できます。シンプルなキャッシュの例は以下の通りです。
4 安定性のポケット
インターフェイスの安定性が低い場合、通常の対応はコンポーネント層でレスポンスエラーを処理することです。このやり取りのロジックは通常複雑で、コンポーネント層のメンテナンスコストが高くなります。アンチ腐食層で安定性をチェックし、インターフェイスが失敗した場合はフォールバックビジネスデータを返すことができます。フォールバックデータはアンチ腐食層で一元管理されるため、テストと修正がより便利になります。前述の複数バージョン共存のアンチ腐食層に以下のコードを追加することで、v2 と v3 のインターフェイスがデータを返せない場合でも、フロントエンドは利用可能な状態を維持できます。
5 共同デバッグとテスト
インターフェイスとフロントエンドが並行開発されている状態があるかもしれません。この時、フロントエンドの開発には実際のバックエンドインターフェイスが利用できません。従来のモック API を構築する方法と比較して、アンチ腐食層で直接データをモックする方がより簡便なソリューションです。
アンチ腐食層でのデータモックはページのテストにも使用できます。たとえば、大量のデータをモックしてページパフォーマンスへの影響をテストできます。
Related Articles
-
A detailed explanation of Hadoop core architecture HDFS
Knowledge Base Team
-
What Does IOT Mean
Knowledge Base Team
-
6 Optional Technologies for Data Storage
Knowledge Base Team
-
What Is Blockchain Technology
Knowledge Base Team
Explore More Special Offers
-
Short Message Service(SMS) & Mail Service
50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00
