|

ソフトウェア設計パターン: 製品コードの作成から得られる実践的な学習

ペースの速いスタートアップ企業で、環境に優しく、しかし熱心な開発者として、コード ベースの学習と、本番環境ですぐに使用できる保守可能なコードの作成を同時に課せられると、El Capitan を真っ直ぐ見つめているような気分になることがあります。 Rescale では、開発者には、特定の機能を実装するためのソフトウェア設計の選択において幅広い自由度が与えられています。 最近、複数のジョブを受け入れることができる永続クラスターを作成する機能を実装しました。 スケーラブルで保守可能で、アプリケーションのアーキテクチャと整合する最適な設計パターンを選択するには、ある程度の思慮深さが必要です。 この素晴らしい機能に取り組んだ私の経験を共有します。
簡単にまとめると、Rescale の用語における「ジョブ」とは、ソフトウェア (たとえば、機械学習用の Tensorflow や遺伝的アライメント クエリ用の BLAST+ (Rescale が提供する他の多数のソフトウェア パッケージの中でも) など) とハードウェア クラスター (たとえば、 AWS の C4 インスタンス)とジョブ固有のコマンド。 永続クラスター機能が導入される前は、典型的な「基本」使用例では、ジョブが所定の処理を実行した後にクラスターがシャットダウンしていました。 永続クラスター機能を使用すると、ユーザーは正確な要件に従ってクラスターを構築し、XNUMX 日を通して複数のジョブを実行し、完了したらそれを破棄することができます。   
この機能は、Python Django フレームワークを使用して行われた新しい API エンドポイントと、Angular フレームワークを使用して行われたいくつかのビュー (UI) の変更で構成されています。 UI に関する課題の XNUMX つは、クラスター リスト ビュー (「clusterList ビュー」と呼びます) を再利用する方法でした。これは、Angular ディレクティブ (「clusterList ディレクティブ」と呼びます) としてカプセル化されており、以下に示します。
1-再スケール-クラスター
このディレクティブに対応するコントローラーとテンプレートによってカプセル化が完了します。 Angular ディレクティブは、HTML テンプレート、ビュー ロジック、ビジネス ロジックのカプセル化を通じて再利用性を提供します。 このディレクティブの主な役割は、クラスターのリストを表示することであり、クラスターを削除する機能も含まれています。 このビューのコントローラーには、ページネーションの機能と、詳細情報を得るために特定のクラスターに移動する機能が含まれています。
以下は、ハードウェア設定ビューで再利用されるclusterList ディレクティブです (これ自体は、独自のカプセル化されたディレクティブであり、「hardwareSettings ディレクティブ」と呼ばれます)。 ハードウェア設定ビューでは、ユーザーは永続クラスター (ユーザーが以前にスピンアップしたもの) を選択するか、新しいクラスター (以前のワークフロー) を作成するかを選択できます。  

このビューは次のディレクティブで構成されていました (簡潔にするために属性は削除されています)。

ソフトウェア設計の決定は、clusterList ディレクティブから永続クラスターが選択されたことを、hardwareSettings ビューを担当するコントローラーにどのように伝えるかに重点を置きました。 hardwareSettings コントローラーには、Job オブジェクトのハードウェア設定を設定するロジックが含まれています。 永続クラスター機能の追加により、これらのハードウェア設定は永続クラスターまたは新しく生成されたクラスターのいずれかから取得されます。

パブリッシュ-サブスクライブ ('pub-sub') と依存関係の注入
Angular では、$rootscope の $emit 関数と $on 関数を使用して、アプリケーション全体のパブリッシュ/サブスクライブ アーキテクチャを作成できます。

上記のコードは、トピック「clusterSelected」を含むメッセージをクラスター オブジェクトとともに $rootscope 内のイベント バスに送信します。 アプリケーションの他の部分は、$rootscope に挿入し、$on でトピックをサブスクライブすることで、このイベント バスを利用できます。

パブリッシュ/サブスクライブ デザイン パターンは、境界を越えて (この場合はディレクティブ境界を越えて) 通信するための洗練された方法であり、ネットワーク境界を越えて通信するためによく使用されるアーキテクチャです。 Angular での実装の容易さと、タイムラインで作業する際の「とにかくこれを機能させる」という考え方により、適切な設計パターンとして契約が決まりました。    
私が行ったもう XNUMX つの設計上の選択は、ハードウェア設定ビューでクラスター リスト コントローラー (「clusterListCtrl」) を再利用することでした。

この選択により、ハードウェア設定ビューで必要な動作を実現するために追加の関数を使用してクラスターリスト Ctrl を拡張し、コントローラーに不必要な肥大化を加え、コントローラーをディレクティブにさらに結合しました。 私は、この機能の共同開発者であり、ソフトウェア開発の知恵を発揮する傾向のある経験豊富な開発者である Alex Kudlick に設計の選択を提示しました。 pub-sub と依存関係の注入のメリットについて説明しました。

依存性注入
依存性注入は制御の反転と密接に関係しており、依存性フローが反転されるパターンです。 対照的に、pub-sub の例では、依存関係は、トピック「clusterSelected」をリッスンするすべてのサブスクライバーによって作成され、クリックが検出されると、clusterList ディレクティブによって発行されます。依存関係のフローは、ソースから外側に流れます。
依存関係の注入では、以下に示すように、クリック ハンドラー「on-cluster-click」が属性としてclusterList ディレクティブに注入または渡されます。

この属性は、clusterList ディレクティブの分離スコープのプロパティになりました。 hardwareSettings ビューのコントローラーには、永続クラスターが選択されたときに処理するロジックを含む、toggleClusterSelected と呼ばれる $scope 上の関数が含まれています。 クリックがclusterListディレクティブによって検出されると、toggleClusterSelected関数が呼び出されます。
依存関係注入を使用したクラスターリスト ディレクティブの再利用可能性を、clusterList ディレクティブがクラスター リスト ビューでクリックを処理する方法を示して説明します。

clusterList ビューのコントローラーには、このビューでクリック ハンドラーが実行されるときに呼び出される関数 goToCluster が含まれています。 上記は依存性注入の使用を示しており、さまざまな式 (toggleClusterSelected および goToCluster メソッド) をクリック ハンドラーにバインドすることにより、clusterList ディレクティブは、使用されるコンテキストに応じて異なる動作を持つことができます。  

学習
パブリッシュ/サブスクライブ アーキテクチャを使用すると、イベントを送信し、境界を越えてアプリケーション全体で通信する機能がエレガントかつ簡単に Angular で実現できます。また、疎結合の理想も満たします。 $rootScope.$emit を使用すると、メッセージは Angular アプリケーションの親スコープである $rootScope 経由で送信されました。 これにより、アプリケーションのさまざまな部分のリスナー (hardwareSettings コントローラーとクラスターリスト コントローラーにも 'clusterSelected' のサブスクライバーがありました) が相互に干渉するという問題が発生しました。 たとえば、hardwareSettings ビューでクラスター リストをクリックすると、goToCluster (望ましくない) がトリガーされ、toggleClusterSelected (望ましい動作) がトリガーされます。 ただし、goToCluster が優先したため、正しい動作が実現されませんでした。 この問題の回避策は、アプリケーション全体の $rootScope ではなく、関連するスコープのみにメッセージングを制限する $scope.$emit と $scope.$on を使用することでした。 ただし、複数の加入者が同じメッセージを処理している場合、副作用や干渉が発生する可能性は依然として存在します。  
もう XNUMX つの欠点は、「clusterSelected」メッセージをサブスクライブするコンポーネント、hardwareSettings コントローラーおよびクラスターリスト コントローラーが、clusterList ディレクティブへの暗黙的な依存関係を形成することでした。 将来の開発者は、コントローラーの本体を調べて、「clusterSelected」メッセージへの依存関係があるかどうかを判断する必要があります。 サブスクライバーが XNUMX 人の場合、これは大したことではないかもしれませんが、サブスクライバーがさらに追加されると、このアプローチは保守が負担になり、コードが脆弱になる可能性があります。
依存関係の注入では、依存関係が明示的であるため、開発者はディレクティブ マークアップ内の属性をスキャンするか、ディレクティブ内の分離スコープをスキャンして、依存関係の完全なリストを取得できます。    

さらに、適切な依存関係と式バインディングを指定することで、ディレクティブをさまざまなコンテキストで再利用できます。 ソフトウェア エンジニアとして、保守性と拡張性を念頭に置いて、特定の設計選択の結果を熟考するために時間を割り当てる必要があり、最終的には暗黙的よりも明示的な方が優れていることを学びました。

著者

  • Adam McKenzie

    アダムは CTO として、HPC チームとカスタマー サクセス チームの管理を担当しています。 アダムはボーイングでキャリアをスタートし、787 年間 XNUMX に取り組み、主翼の設計、分析、最適化を行う構造およびソフトウェア エンジニアリング プロジェクトを管理しました。 アダムはオレゴン州立大学で機械工学の学士号を優秀な成績で取得しています。

類似の投稿