5

私は Symfony 2 / Doctrine 2 で、アプリケーションのサービスとコントローラーにあまりにも多くのビジネスロジックを構築し、モデルに十分ではないことに気付きました。

モデルに構成を導入して (動作を変更するため)、モデルが動作を実行するためにサービスに直接アクセスできるようにする可能性があります。

次の質問には、8 票で正しいとマークされた完全に間違った回答があることに気付きました。つまり、これまでに採用したアプローチ (貧血モデル) が、多くの Symfony によって物事を行うための「正しい」方法と見なされていることを知っています。 2 ユーザー。ドメイン駆動設計について詳しく読んだ後、そうではないことがわかりました。

Symfony2 MVC: 私のコードはどこに属していますか?

多くのバンドルがモデルの動作を定義し、エンティティ/ドキュメントでこれを拡張するのを見ます。このパターンはある程度機能しますが、追加の段階を導入する必要があると思います。モデルの動作の一部はオプションであり、その動作は、アプリケーションに登録されている追加のバンドルによって異なります (したがって、X バンドルを含めると、アプリケーションはより多くのことを実行できるようになります)。例。

現時点では、強い依存関係があることを意味するクーリエ バンドル内のエンティティと双方向の関係を持つ注文オブジェクトがあります。これを分離し、クーリエバンドルにオプションで動作を注文に追加させたいと思います。このメソッド呼び出しを検討してください。

// no courier bundle is registered
$order->getShippingMethods();
// throws NoAvailableShippingMethodsException;

// one bundle registered
$order-getShippingMethods();
// returns an array with one shipping method

etc....

現在、Entity Manager の上に置かれている OrderProvider サービスがあります。

$orderProvider->GetOrder($id);

データベースから「直接」返されたエンティティを取得するだけです。ここでの私の質問は、他の人がここで使用しているパターンは何ですか? すべての「ビジネス ロジック」をエンティティが拡張するモデル クラスに移動し、サービス レイヤーにエンティティを引き出させ (エンティティはデータベースとゲッターのプロパティを持つダム レコードです)、構成を使用してモデルを構成することを考えています ( OrderProvider サービスに注入される構成)。これにより、モデルの動作が変更されます。与えられた例では、(OrderProvider内で)のようなことをするかもしれません..

// trimmed down for example purposes by removing exceptions etc.
public function getOrder($id) 
{
    $order = $this->orderRepository->findOneById($id);
    if ($this->couriers){
       $order->addCouriers($couriers);
    }
    return $order;
}

// this function would be called by the courier bundle automatically using semantic configuration / tags / setter injection
public function addCourier(CourierInterface $courier)
{
    $this->couriers[] = $courier;
}

私が持っている他のオプションは、新しいタイプのオブジェクトを作成することです-これは基本注文を装飾し、すでに構成されています(ITSELFはDICでサービスとして定義されるため)、それに注文を挿入します。違いは微妙で、両方のアプローチが機能しますが、どちらが最善の方法なのか疑問に思っています。

最後に、これらすべてについて、頭を悩ませることができない問題が 1 つあります。私の基本注文エンティティが他のエンティティとの関係を持ち、それらのエンティティを構成する必要がある場合、これはどこで行われるべきですか? たとえば、このように顧客にアクセスするとします。

$order->getCustomer();

顧客(エンティティ)を取得します。しかし、顧客オブジェクトにもいくつかの構成を追加する必要がある場合があります-のように

$customer->getContactMethods();

このメソッドの動作は、アプリケーションが twitter バンドルまたは facebook バンドルなどを登録しているかどうかによって異なる場合があります。上記の例では、十分に構成された顧客を取得するつもりはありませんが、「バニラ」の基本エンティティを取得します。これを回避できる唯一の方法は、構成が必要なエンティティ間の関係を切り取り、CustomerProvider サービスからエンティティをプルすることです。

$customerProvider->getCustomerByOrder($order);

これは、モデル レイヤーから情報を削除しているように思われ、単純なタスクに複数のサービスを使用することへの依存に戻ります (これを回避しようとしています)。リソースへの考えとリンクを歓迎します。

編集:関連 - 毎日最初の回答にリストされている短所を目にするので、この質問をしました->貧血ドメインモデル:長所/短所

4

1 に答える 1

1

プロジェクトの複雑さはモジュール性要件のようです-アプリケーションの動作はバンドルを介して拡張可能でなければなりません。私は Symfony 2 / Doctrine 2 には詳しくありませんが、典型的な DDD 戦術は、Order や Customer などのドメイン エンティティがバンドル構成を認識しないようにすることです。つまり、周囲のサービスはエンティティにバンドル固有の動作を追加するべきではありません。バンドル認識の責任をエンティティに委任すると、エンティティが複雑になりすぎます。広範な動作をサポートするためにエンティティ クラス階層を作成することも複雑すぎます。代わりに、この拡張性はアプリケーション サービスによって管理される必要があります。アプリケーション サービスは、どのバンドルが読み込まれるかを判断し、結果として適切なエンティティを調整します。

考慮すべきもう 1 つの戦略的パターンは、境界付けられたコンテキストです。アプリケーションをモジュールに合わせて境界付けられたコンテキストに分割することは可能ですか? たとえば、$order-getShippingMethods()メソッドに対処するために、2 つの BC を作成できます。1 つはメソッドを持つ注文モデルがあり、もう 1 つはgetShippingMethods()メソッドを持たない注文モデルです。2 つのモデルを持つことはDRYに違反しているように見えるかもしれませんが、モデルが異なるもの (つまり、配送データのある注文とない注文) を表す場合、実際には何も繰り返されません。

于 2012-12-15T19:48:15.917 に答える