7

データ アクセスに Doctrine を利用するビジネス ルールで構成されるモデル レイヤーを作成する次のアプローチについて、フィードバックをお寄せください。

私の現在のアプローチは、Model が ContainerAware クラス/オブジェクトであり、ライブラリ以外のビジネス固有のドメイン ロジックがすべて含まれているという概念に基づいています。

このように物事を行うには、フレームワークを叩く必要があることがわかりました。そのため、私の脳の一部が私のアプローチに疑問を呈しています。

私は現在 Symfony 2 を使用しています。これは、最新のすべての PHP MVC フレームワークと同様に、Doctrine 2 のような ORM レイヤーを利用し、必然的にそれをモデル レイヤーと見なします。ZF2でも状況は似ていると思いますので、私の例はSF2で書かれていますが、これはフレームワークにとらわれない質問と考えてください。

具体例

具体的な例として、次のシナリオを考えてみましょう。

メッセージ要件

  • ユーザーとして、自分に属するメッセージを作成できます。
  • ユーザーとして、自分に属するメッセージを更新できます。
  • ユーザーとして、自分に属するメッセージをアーカイブできます。

コントローラー

Symfony2 では、これらの要件はコントローラー層のアクションとしてコード化されています。 メッセージが実際にユーザーに属しているかどうかをチェックする余分なコードはスキップしますが、明らかに、これもドメイン ロジックの一部である必要があります。メソッド「belongsToUser」など。

 // Vendor\MessageBundle\DefaultController 
 public function archiveAction(Request $request) {
    // ... 
    $em = $this->getDoctrine()
               ->getManager();
    $message = $em->getRepository('MessageBundle:Message');
                    ->getManager()
                    ->getRepository('MessageBundle:Message')
                    ->find($request->get('id'));
    $message->setIsArchived(true);
    $em->persist($entity);
    $em->flush();
    $this->flashMessage('Message has been archived.');
    // ...
}

モデル

これをモデルに入れると、次のようになります。

class MessageModel 
{
     public function archive($messageId) {
     // ... 
        $em = $this->getDoctrine()
                   ->getManager();
        $message = $em->getRepository('MessageBundle:Message')
                    ->find($messageId);
        $message->setIsArchived(true);
        $em->persist($entity);
        $em->flush();
     // ... return true on success, false on fail.
     }
}

改訂されたコントローラー

改訂されたコントローラーは次のようになります。

 // Vendor\MessageBundle\DefaultController 
 public function archiveAction(Request $request) {
    // ... 
    $model = new MessageModel(); // or a factory.
    $result = $model->archive($request->get('id'));
    if($result) {
        $this->flashMessage('Message has been archived.');
    } else { 
        $this->flashMessage('Message could not be archived due to a system error.');
    }
    return array('result'=>$result);
    // ...
}

他の 2 つの要件もモデルに実装されます。

一言で言えば私のアプローチ

一言で言えば、これは私の現在のアプローチです:

  • コントローラー- 少ないロジック
  • ビュー- 同じまま
  • モデル- コンテナを認識し、すべてのビジネス ロジックを収容し、Doctrine にデータ アクセスとしてアクセスします。
  • ORM - モデル層の一部と見なされますが、モデル層とは見なされません。
  • サービス レイヤー- 必要に応じて、サービス レイヤーを使用して複数のレイヤーを操作できますが、構築しなければならなかったアプリケーションの性質が単純であるため、サービス レイヤーを使用する必要があるのはごくわずかであることがわかりました。 .

私の質問

  • 私のアプローチは、他の人が行っていることと一致していますか?
  • 明らかに明らかな何かが欠けていますか?
  • これに似たものを試してみて、良い/悪いと感じましたか?

前もって感謝します。

4

1 に答える 1

4

最初に、ParamConverterを使用してコントローラーを簡素化できます。エンティティが見つからない場合、例外が自動的にスローされます。

メッセージメソッドを呼び出しますarchiverestore、それは好みの問題です。

JMSSecurityExtraBundleは、セキュリティ チェックを統合する優れた方法を提供します。ACLを使用して、現在のユーザーがメッセージのアーカイブを許可されているかどうかを確認します。

これは、 FOSUserBundleで見つけることができる(インターフェース/抽象、つまりドクトリン ODM/ORM実装MessageModel) パターンに似ています。Manager

これらのマネージャーは、ストレージ レイヤーとコントローラー間のブリッジを構築し、すべて同じインターフェイス (つまりUserManagerInterface) を共有します。このようにして、doctrine と propel を簡単に交換できます。

Managerコンテナ全体を注入して (つまり、 を拡張してSymfony\Bundle\FrameworkBundle\Controller\Controller) そこから取得するのではなく、コントローラーをサービスに変換してサービスを注入することで改善します。

テストを容易にするために、必要な依存関係のみをサービスに挿入する必要があります。doctrine orm/odm の例では、マネージャ サービスは classname パラメータ、entity-/documentmanager サービス、およびリポジトリ サービスを取得します。(サービス定義)

Benjamin Eberlei のブログ投稿「Symfony2 の拡張: コントローラー ユーティリティ」で、コントローラー ユーティリティ サービスを作成するための追加のインスピレーションを見つけることができます。

よく使用されるもう 1 つの手法は、最も一般的に使用されるコントローラーの依存関係に対して抽象的な親サービスを作成することです。

私が持っている最後の簡単なヒントは、フラッシュメッセージの作成をイベントリスナー/サブスクライバーに移動することです. Messageメソッドが型以上のオブジェクトを返すかどうかを確認しMessageInterface、success-flashmessage を追加します。エンティティが見つからない場合は、例外をキャッチして、エラー メッセージとともにフラッシュ メッセージを追加できます。

メソッドが何も返さない場合に元の引数を返すと自動的に想定するFOSRestBundle のreturnようなビュー応答リスナーを使用する場合は、メソッドの最後にある を省略することもできます。詳細については、こちらを参照してください

最後に、次のような適切にテスト可能なコントローラー サービスのメソッドを作成できます。

/** 
 * @PreAuthorize("hasPermission(#message, 'ARCHIVE')")
 */
public function archiveAction(Message $message) 
{
    $message->archive();
    $this->messageManager->update($message, true);
}

このmanager->update()メソッドは、私の例のFOS\UserBundle\Doctrine\UserManagerにあるものと同じように動作します。

于 2013-11-07T23:23:02.240 に答える