1

次の (Laravel PHP) プロジェクトで実装したいリポジトリサービス/ユースケースパターン ( DDD設計の一部)に関して頭を悩ませていることについて助けが必要です。

すべてが明確に見えます。紛らわしい DDD の一部は、リポジトリのデータ構造です。人々は、リポジトリが返すデータ構造 (配列またはエンティティ) を選択しているようですが、すべてに欠点があります。その一つが、自分の過去の経験を振り返るパフォーマンスです。1 つは、単純なデータ構造 (配列または単純なオブジェクト属性) 用のインターフェイスがないことです。

以前のプロジェクトでの経験を説明することから始めます。このプロジェクトには欠陥がありましたが、いくつかの優れた長所を学び、新しいプロジェクトで見たいと思っていますが、いくつかの設計ミスを解決しています.

以前の経験

過去に、Kohana フレームワークと Doctrine 2 ORM (データ マッパー パターン) を使用して API 中心の Web サイトを構築しました。フローは次のようになりました。

ウェブサイト コントローラー → API クライアント (HMVC 呼び出し) → API コントローラー → カスタム リポジトリ → Doctrine 2 ORM ネイティブ リポジトリ/エンティティ マネージャー

私のカスタム リポジトリは、Doctrine2 DQL を使用してプレーンな配列を返しました。Doctrine2 は、読み取り専用操作の配列結果データを推奨しています。そして、はい、それは私のサイトを素晴らしく軽くしました. API コントローラーは配列データを JSON に変換しました。そのような単純な。

過去に、私の会社は読み込まれた Doctrine2 エンティティに完全に依存するプロジェクトを作成しましたが、パフォーマンスのために後悔していました。

私の REST API は
/api/users?include_latest_adverts=2&include_location=true
、users リソースのようなクエリをサポートしていました。include_locationロケーション関係を直接含むリポジトリに渡される API コントローラー。latest_adverts=2コントローラーは、広告リポジトリを読み取って呼び出し、各ユーザーの最新の 2 つの広告を取得しました。配列が返されました。

たとえば、最初のユーザー配列:

[
    name
    avatar
    adverts [
        advert 1 [
            name
            price 
        ]
        advert 2 [
            …. 
        ]
    ]
]

これは非常に成功したことが証明されました。私のウェブサイト全体が API を使用していました。API はすでに oauth を使用して完全に運用されているため、新しいクライアントを追加するのは非常に簡単です。ウェブサイト全体がその上で実行されます。

しかし、この設計にも欠陥がありました。has_adverts=true私のコントローラーには、検証、メール送信、パラメーター、またはユーザーに広告のみを表示するためのフィルターのための多くのロジックがまだ含まれていました。まったく新しい CLI インターフェイスなどの新しいポートを作成した場合、すべての検証などのためにこれらのコントローラーを大量に複製する必要がありますが、新しいクライアントを作成する場合は複製しません。それで、少なくとも1つの問題が解決されました:-)

私の管理パネルは、doctrine2 リポジトリ/entity-manager に完全に結合され、開発をスピードアップしました (一種の)。なんで?私のAPIには、ウェブサイト専用の特別な機能(特別な検証、登録のためのメール送信など)を備えたファットコントローラーがあったためです。作業をやり直したり、リファクタリングしたりする必要がありました。そこで、エンティティを直接使用して、たとえばすべての API コントローラーを書き直してサービス (サイトと管理者用) に移動する代わりに、何らかの明確な方法でコードを記述することにしました。設計ミスを修正するには時間が問題でした。

次のプロジェクトでは、すべてのコードを独自のカスタム リポジトリとサービスを通過させたいと考えています。良好な分離のための 1 つのフロー。

新しいプロジェクト (DDD のアイデアを使用) とデータ構造のジレンマ

私は API 中心であるという考えは好きですが、HTTP プロトコルを介さずに同じ機能を使用できるようにする必要があるため、次のプロジェクトのコアを API 中心にしたくはありません。DDDのアイデアでコアを設計したい。

しかし、API として機能し、単純な配列を返すレイヤーを使用するというアイデアは気に入りました。私自身のフロントエンドを含む、新しいポートの完璧なベースです。私の考えは、Service クラスを API インターフェイス (配列データを返す) と見なし、検証などを行うことです。Web サイト専用のサービス (登録) と、管理者またはバックグラウンド プロセスで使用される単純なサービスを使用できます。一部の管理ケースでは、単純な CRUD 編集にサービスが必要ない場合もあります。リポジトリを直接使用することもできます。コントローラーは非常に薄いでしょう。これにより、実際の REST API を作成することは、フロントエンド コントローラー クラスと同じサービスを使用して新しいコントローラーを作成するだけの問題になります。

ビジネス ルールのような内部ロジックの場合、リポジトリの配列の代わりにエンティティ (明確なインターフェイス) を使用すると便利です。このようにして、属性に基づいたロジックを実行するいくつかのメソッドを定義することでメリットが得られます。しかし、Doctrine2 を使用していて、リポジトリが常にエンティティを返すと、アプリケーションのパフォーマンスが大幅に低下します!!

1 つのデータ構造はパフォーマンスを保証しますが明確なインターフェイスはありません。もう 1 つのデータ構造は明確なインターフェイスを保証しますが、Doctrine 2 のようなデータ パターン パターンを使用するとパフォーマンスが低下します (現在または将来)。また、混乱を招く 2 つのデータ型になってしまう可能性もあります。

私はこの流れに似た何かを考えていました:

Controller (シン) → UserService (検証を含む) → UserRepository (ストレージのみ) → Eloquent ORM

なぜ Doctrine2 ではなく Eloquent なのですか? Laravel フレームワークとコミュニティ内で共通していることに少しこだわりたいからです。そのため、たとえば、モデルに基づいて管理インターフェイスなどを生成する (DDD ルールをバイパスする) など、サードパーティのモジュールを利用できます。サードパーティのモジュールを使用する以外に、切り替えが常に簡単で、データ構造の選択やパフォーマンスに影響を与えないように、コアのものを設計します。

Eloquent はアクティブレコードのパターンです。したがって、このデータを Doctrine2 エンティティのような POPO に変換したくなるでしょう。しかし、いいえ... 上で述べたように、doctrine2 の実際のモデルを使用すると、システムが非常に太くなります。そのため、単純な配列に戻ります。これを知ることは、将来の両方と他の実装で機能します。

しかし、常に配列に依存するのは気分が悪いです。特に社内のビジネス ルールを作成する場合。開発者は、配列の値を推測する必要があり、IDE にオートコンプリートがなく、Entity クラスのような特別なメソッドを持つことができませんでした。しかし、データを処理する方法を 2 つ作るのも気分が悪いものです。または、私はあまりにも完璧主義者です ;) すべてに対して 1 つの明確なデータ構造が必要です!

インターフェイスと POPO を構築することは、多くの重複作業を意味します。Eloquent モデル (エンティティではなく単なるテーブル マッパー) を、このインターフェイスを実装するエンティティ オブジェクトに変換する必要があります。すべて余分な作業です。最終的に、最後のレイヤーは API のようになり、再び配列に変換されます。これも余分な作業です。配列は再び取り引きのようです。

DDD と Hexagonal を読み取るのはとても簡単に思えました。それはとても論理的なようです!しかし、実際には、OOP の原則に固執しようとしているこの 1 つの単純な問題に苦労しています。配列を使用したいのは、パフォーマンスなどに関して、モデルの選択や ORM からのクエリの選択に依存せず、ビューまたは API の配列に変換する際に重複する作業がないことを 100% 確実にする唯一の方法だからです。しかし、ユーザー配列がどのように見えるかについての明確な契約はありません。これらのパターンを使用してプロジェクトをスピードアップしたいのですが、遅くするのではありません:-)したがって、多くのコンバーターを持つオプションはありません。

今、私はたくさんのトピックを読みました。Doctrine2 のような適切なエンティティに準拠する POPO とインターフェースを作成することができますが、Eloquent のすべての余分な作業が必要です。Doctrine2 への切り替えはかなり簡単ですが、パフォーマンスに大きな影響を与えるか、Doctrine2 配列データをこれらの独自のエンティティ インターフェイスに変換する必要があります。単純な配列を返すことを選択する人もいます。

Eloquent の代わりに Doctrine2 を使用するよう人々を説得する人もいますが、Doctrine2 は重く、読み取り専用操作には配列の結果を使用する必要があるという事実を無視しています。

リポジトリは変更可能に設計されていますよね?デザインだけが「いい」からではありません。では、パフォーマンスや重複する作業に大きな影響を与える場合、完全なエンティティに頼ることができるでしょうか? Doctrine2 のみ (結合) を使用している場合でも、そのパフォーマンスのために同じ問題が発生します!

すべての ORM 実装は配列を返すことができるため、重複した作業はありません。良い成果。しかし、私たちは明確な契約を欠いています。そして、配列またはクラス属性のインターフェースはありません (回避策として)...うーん;)

私たちのプログラミング言語で欠落しているブロックを見逃すだけですか? 単純なデータ構造のインターフェース??

すべての配列を作成し、高度なビジネス ロジックをこれらの配列と対話させるのは賢明でしょうか? したがって、明確なインターフェースを持つクラスはありません。事前計算されたデータ (通常は Entity メソッドによって返されます) は、Service クラスで定義された配列キー内にあります。賢明ではない場合、上記のすべてを考慮した代替手段は何ですか?

パフォーマンスやさまざまな ORM の実装などを考慮して、この「ドメイン」で優れた経験を持つ人が、これにどのように対処したか教えていただければ幸いです。

前もって感謝します!

4

1 に答える 1