導入事例

Model-View-Controller(MVC) パターンは、数十年にわたり Web アプリケーション開発の礎となりました。しかし、アプリケーションは複雑さとユーザー需要の増加で成長しているため、データとビジネスのロジックを担当するレイヤーがボトルネックになるという多くのチームが発見しています。Poorly 構造化されたモデルは、タイトなカップリング、重複したロジック、および変化に抵抗するコードベースにつながります。スケーラビリティを実現するには、デリブレーション、懲戒められたモデル設計が必要です。これにより、MVC の包括的なモデルの図面作成と検証が最適に行われ、MVC のモデルの図面作成の包括的な作業が行われます。

MVCパターンの理解

MVC パターンは、アプリケーションを 3 つの相互接続コンポーネントに分離します。

  • モデル:]]データ、ビジネスルール、および持続的なロジックを管理します。 アプリケーションドメインの真理の1つのソースです。
  • []ビュー:[]]]] 一般的に、モデル(またはそれのプレゼンテーションに焦点を当てた表現)からデータを読み込み、ユーザーインターフェイスをレンダリングします。
  • [ コントローラ:]]] ユーザ入力、モデルとビュー間の相互作用をオーケストラ化し、それに応じて状態を更新します。

ビューとコントローラーが重要である一方で、モデルは、最も知的複雑さが残る場所です。 適切に構成されたモデルは、アプリケーションが新しい要件に適応し、トラフィックの増加を処理し、複数のインタフェース(例、Web、API、モバイル)をカスケーディング変更なしでサポートすることを可能にします。

Scalable モデルのコア原則

特定のパターンに潜入する前に、いくつかの基礎原則を内包することが不可欠です。

  • [単体責任:]]]各モデルまたはクラスは、変更する1つの定義された理由を持つ必要があります。例えば、ビジネス検証からデータアクセスを分離します。
  • []:[]]]の懸念の分離は、アプリケーション(永続性、検証、通知など)の異なる側面は、明確に結合された層で実装する必要があります。
  • [[DRY]を繰り返しません。]複数のモデルやコントローラーの重複ロジックは、メンテナンスのナイトマーにつながります。代わりに、再使用可能なサービスやトレイトに一般的な動作を抽出します。
  • [] 依存性反転:[ 高度レベルモジュールは、具体的な実装ではなく、抽象化(インターフェイス)に依存すべきです。これにより、ビジネスロジックを書き換えることなく、データベース、キャッシュプロバイダー、または外部サービスを変更することができます。

ドメイン駆動設計(DDD)

エリック・エヴァンスのドメイン・ドライブ・デザインは、モデルのスケーラビリティに最も効果的なアプローチの1つです。 DDDは、開発者がコアビジネスドメインの周りのモデルを整理することを奨励しています。

ユビキタス語

開発者、ドメインの専門家、および利害関係者が共有する一般的な語彙を確立します。コード、ドキュメント、会話の同じ条件を使用します。例えば、eコマースアプリケーションには、実際の注文行動を反映した[クラスが、一般的なではありません。

境界コンテキスト

大規模なアプリケーションは複数のサブドメインで構成されます。 DDDは、コンテキスト間で明確な境界を定義することを推奨しています。たとえば、注文管理、在庫、出荷のための別のモデルです。 各境界コンテキスト内で、モデルは境界線を越える概念を漏らさずにその特定のドメインのために最適化することができます。 この分離は、開発チームを独立してスケーリングするための鍵です。

リソース

集計は、単一のユニットとして扱われるドメインオブジェクトのクラスターです。 根本的なエンティティティは一貫性を保証します。 例えば、 [] の集計には、 と ] のエンティティティが含まれている場合があります。 このパターンは複雑な関係を減らし、トランザクションを簡素化します。

より深いダイブについては、【】] を参照ください。 マーティン・フフローラーのDDD への導入。

層構造アーキテクチャ

レイヤードアーキテクチャは、モデルを異なる論理層に整理することにより、懸念をさらに分離します。

  • [ドメインレイヤー:]]は、ビジネスエンティティ、値オブジェクト、ドメインサービスが含まれています。 このレイヤーは、インフラストラクチャに依存しません。
  • []アプリケーションレイヤー:[]] オーケストラは、使用例を使用して、ドメインオブジェクトを座標化し、トランザクションを管理します。ドメインレイヤーによって異なります。
  • [インフラレイヤー:[]]]は、持続性、メッセージング、外部API呼び出し、およびその他の技術的な懸念を実装します。ドメインとアプリケーションレイヤーによって異なります。
  • [ プレゼンテーションレイヤー:[]] インターフェイスを介してアプリケーションレイヤーと対話するコントローラーとビュー。

この分離により、データベース技術、キャッシュ戦略、またはUIフレームワークの変更が、コアビジネスロジックを介したくさびないことを確認します。また、ユニットテストが容易になります。データベースをモックすることなく、ドメインロジックをテストできます。

リポジトリとサービス

モデルはきれいで、拡張可能保つために特に2つのパターンは貴重です:

リポジトリパターン

リポジトリは、データアクセスロジックをカプセル化し、ドメインオブジェクトにメモリ内コレクションのようなインターフェイスを提供します。 コントローラ全体でデータベースクエリをスプリンクする代わりに、を呼び出します。 この抽象化は、データソース(MySQLからPostgreSQL、またはテスト用のメモリストアなど)を最小限に抑えることを可能にします。

サービス層

サービスは、自然に単一のエンティティティティに属さないビジネスロジックが含まれています。例えば、[[は、注文を置くときに検証、価格設定、在庫チェックを調整するかもしれません。サービスはリポジトリやドメインのエンティティティに依存しますが、データベースのアグノスティックのままです。この分離はまた、コントローラー、バックグラウンドジョブ、API間で再利用を容易にします。

読み続けて、【]】フローラーのリポジトリパターンの説明[を参照してください。

データ転送オブジェクト(DTO)とモデルの表示

完全なドメインモデルをビューレイヤーまたは外部APIクライアントに公開すると、タイトなカップリングが作成され、不要な内部の詳細が頻繁に表示されます。代わりに、DTOを使用してデータを正確に必要に応じて形作ります。利点は次のとおりです。

  • :]]] ドメインエンティティティの変更は、API クライアントを自動的に破棄しません。
  • []セキュリティ:]] センシティブフィールド(内部ID、監査タイムスタンプ)を省略できます。
  • 性能:]] DTOは、特定のエンドポイントで必要なフィールドだけを、ペイロードサイズを削減する調整することができます。

モデルを見ると、ビュー・モデルは、ビュー・ニーズをレンダリングするデータのみを含むプレゼンテーションレイヤーの同様の目的(フォーマットされた日付や計算された合計のような表示ロジックと組み合わせて)を提供します。

拡張性のためのデータベースアクセスの最適化

データベースアクセスが非効率の場合、最もクリーンなモデルアーキテクチャでも失敗します。 主な戦略は次のとおりです。

インデックス

、、 で使われる列のインデックスを作成し、節を解析します。 オーバーインデックスは書き込みを遅くする可能性があるので、測定および監視します。

クエリキャッシュ

レジスやMemcachedなどのインメモリーストアを使用して、高価なクエリの結果をキャッシュします。ドメイン(タイムベース、イベント主導、またはマニュアル)に適したキャッシュの無効化を実行します。

パッジネーションとレイジーローディング

メモリに大きなデータセットをロードしないでください。カーソルベースまたはオフセットのペジネーションを使用します。ORMsでは、子関係の怠惰な読み込みを有効にしますが、必要に応じてN+1クエリの問題に注意する必要があります。Eagerローディング(例:)をActiveRecordまたは)をSQLで使用してください。

レイジーローディング対エイジャーローディング

正しいロード戦略を選択すると、パフォーマンスが重要である:

  • []レイジーローディング:[]]] 関連するデータはアクセス時にのみ読み込まれます。 これは、単一エンティティティの操作に有効ですが、ループ(N+1の問題を読まれた)でパフォーマンスを劣化させることができます。
  • エイジャーローディング:]] 必要なすべての関係を単一のクエリでロードします。ビューやサービスが関連データを必要とすることを知ったときに使用します。多くのORMは、明示的なエイジャーロードまたは予測をサポートしています。

実用的アプローチは、既知のパスのロードを熱し、アクセスが困難な関連付けのためにのみ怠惰な読み込みを使用するデフォルトです。 適切なバランスを見つけるために、現実的な負荷の下でデータベースのクエリをプロファイルします。

水平スケーリングの企画

アプリケーションが単一のサーバーを超えて成長する場合、モデルレイヤーは配布をサポートする必要があります。

  • [Stateless モデル:[]]] は、モデルインスタンスにユーザーセッションやリクエスト固有のデータを格納しないようにします。 依存関係の注射を使用して、ステートレスサービスを提供します。
  • []効率的なシリアライズ:[ネットワークを横断するモデル(JSON API経由で)は、高速シリアライズ/デシリアライゼーションのために設計する必要があります。 円参照で複雑なオブジェクトグラフではなく、DTOを使用してください。
  • [データベースシャード:]] 非常に大きなデータセット、複数のデータベース間での分割データ。 リポジトリレイヤーは、集計ルートに基づいてルーティング戦略で、シャードロジックを抽象化する必要があります。
  • []:イベントの一貫性:[]]] 分散システムでは、リソースをサービス全体にロックする分散トランザクションを避けます。代わりに、イベントやメッセージキューのようなイベント主導のパターンを使用して、イベントの一貫性を埋めます。

追加のベストプラクティス

依存症の注入

依存関係の注入コンテナを使用して、リポジトリとサービス依存関係を解決します。このデカップリングモデルは、コンクリートの実装から構築され、テストやスケーリングのためにコンポーネントを交換するのを試みます。

不変性

可能であれば、オブジェクトをimmutableとして設計します。immutable [クラスは、エイリアスやコンポレーションに関連するバグを減少させます。さらに、immutableモデルはテストとキャッシュが容易です。

分離のテスト

サービスとドメインロジックのユニットテストでは、データベースやフレームワークのブートストラップを必要としません。 モックリポジトリまたはメモリ内の実装を使用してください。 統合テストは、実際のデータベースに対する持続的な動作を検証できますが、ターゲットを絞ったままにすることができます。

反回収層

従来のシステムや外部APIと統合すると、モデルと外部システムモデル間でトランスレーションする防腐層を構築します。これにより、外部のドメインへの漏洩を防ぐことができます。

ドキュメントとコードのレビュー

多くの場合、モデル構造は不透明になる。 アーキテクチャの決定レコード(ADR)を維持し、コードレビューを通じて一貫性を強化します。 よくドキュメント化されたモデルは、新しいチームメンバーをオンボードしたり、モジュールの月後に再訪するときに配当を支払います。

コンテンツ

MVCパターンのスケーラビリティモデルを監視することは、ワンタイム設計の演習ではなく、継続的な規準です。懸念の分離、DDDおよびレイヤーアーキテクチャを適用し、リポジトリ、サービス、DTOを使用して、さまざまな原則に付着することで、アプリケーションで成長できるモデルレイヤーを作成します。適切なロード戦略を選択し、水平スケーリングを計画することで、アプリケーションがロードされることなく実行されるようにします。 決定書は、 pstay に含まれ、あらゆるアーキテクチャを検証し、その決定を把握し、その決定を把握します。

さらなる調査のために、 ] の領域主導のデザインブック と [] の学習パターン]] を検討してください。 これらのリソースは、ここで議論されたパターンに深く洞察を提供します。