web-dev-qa-db-ja.com

ドメイン駆動設計とファクトリークラスの役割

ファクトリークラスの役割と責任が何であるかは不明です。ファクトリクラスが、関連するエンティティおよび値オブジェクトとともにドメインオブジェクト(集約ルート)の作成に責任を持つ必要があることは十分に承知しています。

しかし、工場の「レイヤー」がDDDアーキテクチャーのどこにあるのか、私には明確ではありません。ファクトリは、データまたはサービスライブラリを取得するためにリポジトリを直接呼び出す必要がありますか?

ファクトリーは次のフレームワークにどこに適合しますか:
UI>アプリ>ドメイン>サービス>データ

また、ファクトリはオブジェクトの作成に許可されている唯一の場所であるため、データレイヤーとサービスレイヤーにオブジェクトを作成する場合は循環参照を取得しませんか?

ファクトリクラスの役割がオブジェクトの作成である場合、サービスレイヤーにはどのような利点がありますか?

私は多くの質問をしました、そしてどんな返事にも感謝します。私が不足しているのは、ドメイン主導の設計プロジェクトのすべてのレイヤーがどのように統合されるかを示すサンプルアプリケーションです...何かありますか?

45
Th3Fix3r

しかし、工場の「レイヤー」がDDDアーキテクチャーのどこにあるのか、私には明確ではありません。ファクトリは、データまたはサービスライブラリを取得するためにリポジトリを直接呼び出す必要がありますか?

ファクトリは、ドメインオブジェクトを構築するためのワンストップショップでなければなりません。これを行う必要があるコードの他の部分では、ファクトリを使用する必要があります。

通常、ドメインオブジェクト構築のためのファクトリへの入力として使用されるデータのソースは少なくとも3つあります。UIからの入力、永続性からのクエリの結果、ドメインに意味のある要求です。したがって、特定の質問に答えるために、リポジトリはファクトリを使用します。

例を示します。ここでは Holubのビルダーパターン を使用しています。 Edit:このパターンの使用を無視します。私はそれが DDDファクトリーとうまく混合されていない であることを理解し始めました。

// domain layer
class Order
{
    private Integer ID;
    private Customer owner;
    private List<Product> ordered;

    // can't be null, needs complicated rules to initialize
    private Product featured; 

    // can't be null, needs complicated rules to initialize, not part of Order aggregate
    private Itinerary schedule; 

    void importFrom(Importer importer) { ... }

    void exportTo(Exporter exporter) { ... }

    ... insert business logic methods here ...

    interface Importer
    {
        Integer importID();
        Customer importOwner();
        Product importOrdered();
    }

    interface Exporter
    {
        void exportID(Integer id);
        void exportOwner(Customer owner);
        void exportOrdered(Product ordered);
    }
}

// domain layer
interface OrderEntryScreenExport { ... }

// UI
class UIScreen
{
    public UIScreen(OrderEntryDTO dto) { ... }
}

// App Layer
class OrderEntryDTO implements OrderEntryScreenExport { ... }

OrderFactoryは次のようになります。

interface OrderFactory
{
    Order createWith(Customer owner, Product ordered);
    Order createFrom(OrderEntryScreenExport to);
    Order createFrom(List<String> resultSets);
}

注目の製品のロジックと旅程の生成は、OrderFactoryで行われます。

次に、各インスタンスでファクトリを使用する方法を示します。

OrderRepository:

public List<Order> findAllMatching(Criteria someCriteria)
{
    ResultSet rcds = this.db.execFindOrdersQueryWith(someCriteria.toString());
    List<List<String>> results = convertToStringList(rcds);

    List<Order> returnList = new ArrayList<Order>();

    for(List<String> row : results)
        returnList.add(this.orderFactory.createFrom(row));

    return returnList;
}

アプリケーション層で:

public void submitOrder(OrderEntryDTO dto)
{
    Order toBeSubmitted = this.orderFactory.createFrom(dto);

    this.orderRepo.add(toBeSubmitted);

    // do other stuff, raise events, etc
}

ドメイン層内でのユニットテストは、おそらく次のとおりです。

Customer carl = customerRepo.findByName("Carl");
List<Product> weapons = productRepo.findAllByName("Ruger P-95 9mm");
Order weaponsForCarl = orderFactory.createWith(carl, weapons);

weaponsForCarl.place();

assertTrue(weaponsForCarl.isPlaced());
assertTrue(weaponsForCarl.hasSpecialShippingNeeds());

ファクトリは次のフレームワークにどこに適合しますか:UI> App> Domain> Service> Data

ドメイン。

また、ファクトリはオブジェクトの作成に許可されている唯一の場所であるため、データレイヤーとサービスレイヤーにオブジェクトを作成する場合は循環参照を取得しませんか?

私の例では、すべての依存関係は上から下に流れます。 依存関係の逆転の原則 (PDFリンク)を使用して、あなたが話す問題を回避しました。

ファクトリクラスの役割がオブジェクトの作成である場合、サービスレイヤーにはどのような利点がありますか?

単一のドメインオブジェクトに適合しないロジックがある場合OR複数のドメインオブジェクトをオーケストレーションするアルゴリズムがある場合は、サービスを使用します。サービスは、適合しないロジックをカプセル化しますそれ以外の場合は、それが適合するドメインオブジェクトに委任します。

私がここで落書きした例では、注文の旅程を作成するには、複数のドメインオブジェクトが関係していると思います。 OrderFactoryは、このようなサービスに委任できます。

ところで、説明した階層はおそらくUI>アプリ>ドメインサービス>ドメイン>インフラストラクチャ(データ)になるはずです

私は多くの質問をしました、そしてどんな返事にも感謝します。私が不足しているのは、ドメイン主導の設計プロジェクトのすべてのレイヤーがどのように統合されるかを示すサンプルアプリケーションです...何かありますか?

ドメイン主導の設計とパターンの適用 ジミー・ニルソン著は、エリック・エバンスの ドメイン主導の設計 に対する素晴らしい賛辞です。レイヤー化に重点が置かれているかどうかはわかりませんが、コード例はたくさんあります。レイヤー化は注意が必要で、DDDとは別のトピックです。

Evansの本には、チェックしたいレイヤーの非常に小さな例があります。レイヤー化はエンタープライズパターンであり、Martin Fowlerは Patterns of Enterprise Application Architecture を書いています。

55
moffdub

リポジトリとファクトリーの違いは、リポジトリーは抽象永続ストレージを表すのに対して、ファクトリーはオブジェクトの作成を担当することです。

たとえば、ユーザーを登録しているとします。ファクトリからユーザーオブジェクトを取得します

IUser user = userFactory.Create(name, email);

次に、それを処理する責任があるリポジトリに渡します。

userRepository.Insert(user);

DDDのファクトリーは、インスタンス化の詳細の新しい抽象化を隠す方法と考えることができます。具象ではなく、非常に効果的にインターフェイスにプログラムできます。

さらに、これにより、リポジトリをエンティティタイプに集中させることができるため、ジェネリックの使用が非常に強力になります。

12
Mathieson

おそらく私自身の危険では、私が本当に行き詰っていない限り、デザインパターンを強調しない傾向があります。私は自分の考えたとおりにシステムを構築し、翌日の意味がわかるまでリファクタリングします。

私は次の場合に工場を使用します。

  • 一部のレイヤーは、一定の規則性を持つオブジェクトを作成する必要があります。 および
  • そのレイヤーだけがいつそれを作成するか、またはどのようなカスタマイズで知っているおよび
  • 他のいくつかのレイヤーだけが、何を作成するかを正確に知っています。

DDDの人たちと私はこれについて議論するかもしれませんが、基本的にはその「ファクトリー」クラスのアイデアはドメインオブジェクトを発行することです。次に、ドメインオブジェクトはデータにアクセスし、作業しているドメインの「モデル」の一部になります。

アプリケーション含む UIなど、継承階層ではありません。

その「種類」の演算子には注意してください。多くの人は、継承を使用できるため、継承を使用する必要があると考えています。多くの場合、集約(「次を含む」または「次を含む」ではなく「次を含む」)の方が適しています。

更新

必ずしも。ドメインが教えてくれることを考えてみましょう。たとえば、「製品が必要です。製品の製品番号がわかっています。製品ファクトリが完了すると、製品を表す完全なオブジェクトが提供されます」のようなものです。 Productオブジェクトを作成した場合、特定の製品を表す有効なProductオブジェクトになるためには何が必要ですか?

3
Charlie Martin

ファクトリは、データまたはサービスライブラリを取得するためにリポジトリを直接呼び出す必要がありますか?

どちらでもいいのですが、可能であれば、必要な情報を直接渡す必要があります。

ファクトリは次のフレームワークにどこに適合しますか:UI> App> Domain> Service> Data

このレイヤーがどこから来ているのかわからない、レイヤーはDDDで修正されていませんが、このスタイルに集中するのが一番いいと思います

UI>アプリ>ドメイン

ドメイン内には複数のタイプのオブジェクトがあり、それらの間の関係についてルールを設定します。

  • 工場には、作業を行うために必要なすべてのものが渡される必要があります。そのため、ほとんどの場合、他のサービスやリポジトリを呼び出す必要はありません。
  • ほとんどの場合、エンティティはリポジトリに接続するべきではなく、代わりにサービス(または他の上位層)がこの作業を担当します。
  • エンティティはサービスを呼び出さないでください。サービスはエンティティ/値オブジェクト/仕様の上に位置し、必要に応じて調整します。
  • ドメイン内のサービスは調整するためにあり、重要なドメイン/ビジネスの動作は含まれていません。

ファクトリクラスの役割がオブジェクトの作成である場合、サービスレイヤーにはどのような利点がありますか?

エリックは本でこれを非常によく説明しているので、私はそれを参照しますが、最終的には、クロス集計の動作や1つの集計にうまく適合しない動作(たとえば、本のアカウントの例)がある場合に最適です。

2
Colin Jack

ファクトリを呼び出して新しいエンティティを作成する責任を持つアプリケーションサービスレイヤーが表示されます。したがって、エンティティファクトリインターフェースはアプリケーションサービスモジュールで定義され、実際の実装は永続化と同じようにインフラストラクチャサービスです。一般的なアプリケーションサービスvsドメインサービスvsインフラストラクチャサービスに関する明確なアイデアについては、これを見るとかなり明確になりました。 Bob Martin Ruby Midwest 2011

1
Chalky