web-dev-qa-db-ja.com

リポジトリレイヤーでファクトリパターンを挿入すると、SRPが壊れますか?

MVCのコンテキストでは、ファクトリーを作成し、そのファクトリーにリポジトリーを注入することがあります。

ファクトリー内でレイヤーとしてリポジトリを使用することは確かに可能ですが、そうすることはアンチパターンなのでしょうか。つまり、Factory Repository-freeを維持することをお勧めしますか?

たとえば、私のQuoteFactoryクラスは

  • quoteクラスの作成
  • Repositoryメソッドの維持と、一般にリポジトリー対応

コード

$repository = new QuoteRepository(111);
$factory = new QuoteFactory($repository);    
$quote = $factory->loadQuote();

class QuoteRepository
{
    function __construct($id)
    {
        $this->id = $id;
        $this->em = DoctrineConnector::getEntityManager();
    }

    function getLineItems()
    {
        $query = $this->em
            ->createQuery('SELECT s FROM... where id = :id')
            ->setParameter('id', $id);

        return $query->getResult();
    }
}

class QuoteFactory
{
    function __construct(QuoteRepository $repository)
    {
        $this->repository = $repository;
    }

    function loadQuote()
    {
        $quote = new Quote();
        $quote->setLines($this->repository->getLineItems());
        return $quote;
    }
}

class Quote
{    
    function setLines(array $lines)
    {
        $this->lines = $lines;
    }
}
3
Dennis

一般的に何かを注入することは悪いことではなく、自動的にSRPを破壊することにはなりませんが(データのフェッチのみを行うクラスと、そこからオブジェクトを構築するクラスがある)、別の問題があります:誤解レイヤー化と抽象化の。

リポジトリレイヤーは、データをドメインモデルにバインドするレイヤーです。そのために別のレイヤーは必要ありません。言うまでもなく、ソリューションは過剰設計されています。

リポジトリに直接見積もりを作成するだけです。特に理由がない限り、ファクトリは必要ありません。


以下のコメントから:

ドメインモデルにデータをバインドするとはどういう意味ですか?あまりに抽象的に聞こえますが、理解する方法が見つかりません。例を挙げていただけますか?

あなたは、どういうわけかあなたのシステムにその方法を見つける純粋なデータを持っています。方法は次のとおりです。

  • SOAP API、
  • REST API、
  • データベース、
  • 読み込まれたファイルの内容
  • ...

このデータは単なるデータであり、他には何もありません。ルールは含まれていません。

次に、ビジネス(ルールのあるアプリケーションの楽しい部分)をコアにしますドメイン。問題は、ドメインが純粋なデータを理解していないことです。純粋なデータを理解するために、データはビジネスオブジェクト、ドメインモデルによって表されるように変換されます。

また、全体的に言って、FactoryとRepositoryを(Repositoryに)効果的にマージしますが、Quoteを別の概念として保持しますか、それともデータがRepositoryにバインドされているため、Quoteを使用しますか?

ファクトリとリポジトリをマージする必要があると言っているのではなく、ファクトリを完全に削除し、QuoteオブジェクトをgetLinesメソッドで直接インスタンス化する必要があると言っています。そして、すでにそれについて話しているので、メソッドの名前をgetQuoteWithLineItemsByIdのようなより良いものに変更するのが賢明かもしれません。

また、Quoteは、リポジトリと直接関係がありません。どうして?リポジトリは、すでに述べたように、純粋なデータを取得してオブジェクトに変換することにより、システムへのゲートウェイとして機能します。

提案された設計:

class QuoteRepository
{
    /**
     * @var \Doctrine\ORM\EntityManager
     */
    private $entityManager;

    public function __construct(\Doctrine\ORM\EntityManager $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    /**
     * @param string $quoteId
     * @return Quote
     */
    public function getQuoteWithLineItemsById($quoteId)
    {
        $query = $this->entityManager
            ->createQuery('SELECT s FROM... where id = :id')
            ->setParameter('id', $quoteId);

        $quote = new Quote();
        $quote->setLines($query->getResult());

        return $quote;
    }
}

class Quote
{
    private $lines = [];

    public function setLines(array $lines)
    {
        $this->lines = $lines;
    }
}

$quoteRepository = new QuoteRepository(DoctrineConnector::getEntityManager());
$quote = $quoteRepository->getQuoteWithLineItemsById('1');

これで、リポジトリが、データベースから取得したデータをドメインモデルQueryに変換します。ビジネスロジックについては何も知りません。ビジネスロジックはドメインモデル内にあります。

生データをビジネスが理解できるエンティティに変換する以外に、別の理由でリポジトリレイヤーも存在します。

  • 永続化の目的で、ビジネスが理解できるエンティティを生データに変換します。

つまり、実際には、自分のデータを含み、ビジネスドメインの機能を持ち、データベースの読み取り/書き込みを行う単一のQuoteRepositoryクラスになる可能性があります。

No。データベースへの読み取り/データベースへの書き込みとデータベースへのデータの変換のみを担当するリポジトリレイヤーになります。

次に、永続性を無視し、SQLについてまったく何も知らず、純粋なPHP(おそらくクラス))であり、すべてのビジネスルール(例:ユーザー名)を含む別のレイヤー(ドメイン)があります。空欄にしたり、32文字より長くしたりすることはできません。

ビジネスオペレーションを扱う場合、ドメインモデルはビジネスルールが保持されることを保証します。ドメインモデルが存在し、リポジトリに送信されて保存される場合、リポジトリはドメインモデルが有効な状態であることを信頼するだけなので、ドメインモデルの状態を気にしません。リポジトリの役割は、モデルを保存することであり、その状態をチェックすることではありません。

7
Andy

厳密に言えば、QuoteFactoryはSRPを壊しません。

  • Repositoryメソッドの維持と一般にRepository-awareは責任ではありません。これはクラスの責任の意味です。
  • 見積もりクラスの作成は、その唯一の責任です。

しかし、QuoteFactoryはその責任を果たしていないようです。

私の理解では、工場は機能レベルで「新しい」エンティティ、「新しい」を作成します。リポジトリから取得したエンティティは「新しい」エンティティではありません。コンストラクターは、メモリ内のエンティティの技術的な表現であるオブジェクトを作成します。

したがって、QuoteFactoryloadQuote()メソッドは気分が悪くなります。ストレージから「古い」見積もりを実際にロードし、QuoteRepositoryに移動するか、実際に一部のラインアイテムから「新しい」見積もりを作成し、createQuote()という名前を付ける必要があります。どちらの場合でも、QuoteFactoryQuoteRepositoryを必要としません。

また、QuoteRepositoryは実際には見積もりではなく、ラインアイテムをロードします。広告申込情報が主に見積もりの​​一部である場合は問題ありません。それ以外の場合は、LineItemRepositoryという名前にしないでください。

2
Philippe