モジュラーモノリス: PoC
モノリスのモジュール化は複雑なプロジェクトです。多くの未知の要素が存在します。リスクを軽減し、重要な知見を得るために役立つのが、早期に実施できるプルーフオブコンセプト(PoC)です。これにより、何を行う必要があるかをより深く理解できます。
モジュール間通信
計画している PoC の一つは、モジュール間通信の PoC です。私たちはモジュールを分離する必要があることを認識していますが、それでもよく定義されたインターフェイスを通じてモジュールが相互に通信できることが必要です。モジュールはファサードクラス(ライブラリが通常行うように)を通じて、またはイベントシステムを通じて通信できます。両方の方法が重要です。
主な疑問は、インターフェイスをどのように定義し、通信チャンネルをどのように設計するかです。
モジュールをプラグアウトして、一部を独立したサービスとして運用できるようにすることが目標の一つです。これにより、将来的に GitLab.com のデプロイが容易になり、主要ドメインのスケールアップが可能になります。この目標を達成する一つの方法は、インターフェイスとして protobuf を使用し、通信チャンネルとして gRPC を使用してモジュール間通信を設計することです。モジュールがプラグインされている場合は、gRPC とシリアライゼーションをバイパスし、プロセス内通信プリミティブを使用します(引き続き protobuf をインターフェイスとして使用)。モジュールがプラグアウトされた場合は、gRPC がモジュール間でメッセージを運ぶことになります。
モジュール境界の強制に Packwerk を使用する
Packwerk は Ruby でモジュール境界の定義と強制を支援する静的アナライザーです。
この PoC のマージリクエスト では、個別のモジュールに分解されたモノリスの可能なディレクトリ構造を示しています。
この PoC は EE エクステンション(および JH も)の問題を解決することも目的としており、コア コードベースのみをロードするか、任意のエクステンションをロードするかに応じて Rails 自動ローダーを調整できるようにしています。
PoC では、Ci:: 名前空間の小さな部分のみを components/ci Packwerk パッケージに移動することも試みました。これは今まで検討された中で最もイテレーティブなアプローチのようです。
Packwerk を採用するために使用できるアプローチは異なります。他に検討された PoC には CI パッケージの大規模な抽出 と 主要な CI クラス 2 つのパッケージへの移動 があります。
3 つの PoC すべてに共通点が多く、Packwerk パッケージと設定の導入から、パッケージで動作する自動ローダーのパスの設定まで含まれています。各マージリクエスト間で異なるのは、最初にどのファイルを移動するかを選択するアプローチです。
PoC の主な目標は:
- GitLab コードベースで Packwerk が使用できるかを理解する。
- 開発者の学習曲線を理解する。
- EE および JH エクステンションのサポートを検証する。
- 段階的なモジュール化を可能にする。
肯定的な結果
- Packwerk は主に Rails コードベースで動作するよう設計されているため、GitLab での使用は非常にシンプルです。
- ドメインコードの整理を MVC パターンではなくモジュール指向にすることができます。Rails 自動ローダーが新しいディレクトリ構造をサポートするために小さな初期変更が必要ですが、この構造は Packwerk によって強制されるわけではありません。その後、新しいトップレベルパッケージ/境界付けられたコンテキストの登録は 1 行のコード変更で済みます。
- PoC で示された正しいディレクトリ構造を使用することで、EE および JH エクステンションを含むすべてのコードをパッケージに含めることができます。
- 段階的なモジュール化が可能であり、強制なしの初期状態からインメモリマイクロサービス環境をシミュレートする完全な分離まで、任意の程度のモジュール化ができます。
- ファイルを Packwerk パッケージに移動することは、必ずしも定数の名前変更を意味しません。
これは長期的には推奨されませんが、ツールが提供する追加の柔軟性です。
- 例:
Ci::モジュールを Packwerk パッケージに抽出している場合、CI ドメインに属していても名前空間がない定数(CommitStatusなど)や異なる名前空間(Gitlab::Ci::など)を持つ定数が存在する可能性があります。 Packwerk はそのような定数がciパッケージ内に移動されても、境界の違反を正しくフラグします。 - RubyAtScale ツールによる Packwerk の拡張機能により、パッケージ内のすべての定数が同じ Ruby 名前空間を共有することを強制できます。最終的にはこれを活用したいと考えています。
- 例:
- RubyAtScale は、エンジニアリング組織としてモニタリングして推進していく必要があるモジュール化と採用に関するメトリクスを追跡するツールも提供しています。
- Packwerk には IDE エクステンション(例: VS Code 向け)があり、(RuboCop のように)違反に関するリアルタイムフィードバックを提供します。開発ワークフロー中に単一パッケージに対して CLI からも実行できます。pre-push Git フックや、コードレビュー中の Danger に統合できます。
課題
これらの課題のいくつかは、Packwerk というツール/アプローチに固有のものではありません。PoC 中に観察され、よりモジュール化のプロセス全般に関連するものです:
- Packwerk パッケージを導入する際に正しいアプローチも間違ったアプローチもありません。開発者が最善の決定を下すためのツールを提供するための明確なガイドラインを定義する必要があります:
- 空のパッケージを作成してファイルを段階的に移動することが良い場合があります。
- すでによく設計されて分離されているコードベースの部分をラップすることが良い場合があります。
- 新しいパッケージをゼロから作成することが良い場合があります。
- コードを別のディレクトリ構造に移動するにあたり、JiHu を関与させる必要があります。JiHu は現在のディレクトリ構造に従ってエクステンションを管理しています。 部分的に移行されたモジュールがある場合は、JiHu が現在の進捗状況を把握していることを確認する必要があります。
- プライバシー/依存チェックが有効になると、Rails コードベースの定数参照は非常に絡み合っているため、Packwerk は多くの違反(RuboCop の TODO のように)をログに記録します。
- パッケージを所有するチームは、パッケージのビジョンを定義する必要があります。
すべての違反が修正されたとき、パッケージはどのような姿になるのか?
これは、パッケージがシステムの コンテキストマップ のどこに位置するかを指定することを意味する場合があります。現在のパッケージが別のパッケージ
Aによってどのように使用されるか、また他のパッケージをどのように使用するか。 - 上記のビジョンは、開発者がこれらの違反を時間をかけてどのように修正すべきかを示すべきです。 特定の定数をパブリックにすべきか? パッケージが別のパッケージを依存関係としてリストすべきか? 一部のシナリオではイベントを使用すべきか?
- チームはそれを行うためのガイダンスが必要になる可能性があります。ドメインについて非常に幅広い理解を持つメンテナーのようなエンジニアチームが、この取り組みでエンジニアリングチームをサポートする必要があるかもしれません。
- パッケージを所有するチームは、パッケージのビジョンを定義する必要があります。
すべての違反が修正されたとき、パッケージはどのような姿になるのか?
これは、パッケージがシステムの コンテキストマップ のどこに位置するかを指定することを意味する場合があります。現在のパッケージが別のパッケージ
- PoC 中、Knapsack の調整と選択的テストに関する CI 設定の変更は無視されました。
フロントエンドのソーティングハット
フロントエンドのソーティングハットは、複数のドメインを組み合わせて GitLab のフルページ(メニューや複数の独立したドメインからのアイテムを含む)をレンダリングするための PoC です。
フロントエンドアセットの集約
フロントエンドアセットの集約は、マイクロフロントエンドの可能な分離のための PoC です。
c955a93f)