ドメイン駆動設計:ドメインサービス、アプリケーションサービス


268

誰かがいくつかの例を提供することにより、ドメインとアプリケーションサービスの違いを説明できますか?また、サービスがドメインサービスの場合、このサービスの実際の実装をドメインアセンブリ内に配置します。そうであれば、そのドメインサービスにリポジトリも挿入しますか?いくつかの情報は本当に役に立ちます。

回答:


358

サービスには、ドメインサービスアプリケーションサービスインフラストラクチャサービスの 3つの種類があります

  • ドメインサービス:カプセル化し 、ビジネス・ロジック自然にドメインオブジェクト内に収まらない、としているんNOT典型的なCRUD操作-それらはに属しますリポジトリ
  • アプリケーションサービス:外部のコンシューマがシステムと通信するために使用します(Webサービスなど)。消費者がCRUD操作にアクセスする必要がある場合、それらはここで公開されます。
  • インフラストラクチャサービス:技術的な問題(MSMQ、電子メールプロバイダーなど)を抽象化するために使用されます。

ドメインサービスをドメインオブジェクトと一緒に維持することは賢明です。これらはすべてドメインロジックに重点を置いています。そしてはい、あなたはあなたのサービスにリポジトリを注入することができます。

アプリケーションサービスは通常、ドメインサービスリポジトリの両方を使用して外部リクエストを処理します。

お役に立てば幸いです。


2
CQRSによってコマンドとクエリをどこに配置しますか?どのサービスがそれらを生成し、どのサービスがそれらを処理しますか?
inf3rno 2014

5
アプリケーションサービスは、「Webサービス」などの技術的な詳細に依存しないものである必要があると思います。それらは、そのようなサービスによって使用されます。ドメイン駆動設計のサービスを
デーモン2015

1
@prograhammer-ドメインサービスの例としては、FundsTransferServiceが挙げられます。ドメインモデルはBankAccountであり、転送には、アカウントオブジェクトに直接適合しないビジネスロジックが含まれる可能性があります(Evans DDDブックから取得)。
BornToCode 2016年

たとえば、Loginuser()はドメインサービスの例です。getUsers()がアプリケーションサービスになる場所はどこですか?
filthy_wizard

認証と多くの場合承認の決定はコアドメインに属していないため、どちらもアプリケーションサービスです。
MauganRa 2017

114

(読みたくない場合は、下部に概要があります:-)

私も、アプリケーションサービスの正確な定義に苦労しています。Vijayの答えは1か月前の私の思考プロセスに非常に役立ちましたが、私はそれの一部に同意しなくなりました。

その他の資料

アプリケーションサービスに関する情報はほとんどありません。集約ルート、リポジトリ、ドメインサービスなどのテーマについては幅広く説明されていますが、アプリケーションサービスについては簡単に説明するか、まったく省略しています。

MSDNマガジンの記事「ドメイン駆動設計の概要」では、ドメインサービスをWCFサービスなどの外部クライアントに変換および/または公開する方法として、アプリケーションサービスについて説明しています。これは、Vijayがアプリケーションサービスについても説明する方法です。この観点から見ると、アプリケーションサービスはドメインへインターフェースです。

オニオンアーキテクチャ上のジェフリー・パレルモの記事(パート123は)良い読み物です。彼は、アプリケーションサービスを、ユーザーのセッションなどのアプリケーションレベルの概念として扱います。これはアプリケーションサービスについての私の理解により近いものですが、それでも、このテーマに関する私の考えと一致していません。

私の考え

アプリケーションサービスは、アプリケーションによって提供される依存関係と考えるようになりました。この場合、アプリケーションはデスクトップアプリケーションまたはWCFサービスです。

ドメイン

例の時間。ドメインから始めます。外部リソースに依存しないすべてのエンティティとドメインサービスがここに実装されます。外部リソースに依存するドメインの概念は、インターフェースによって定義されます。以下は可能なソリューションレイアウトです(太字のプロジェクト名)。

私の解決策
- My.Product.Core(My.Product.dll)
  -DomainServices
      IExchangeRateService
    製品
    ProductFactory
    IProductRepository

ProductそしてProductFactoryクラスは、コアアセンブリに実装されています。これIProductRepositoryはおそらくデータベースによって支えられているものです。これの実装はドメインの問題ではないため、インターフェースによって定義されます。

ここでは、に焦点を当てますIExchangeRateService。このサービスのビジネスロジックは、外部Webサービスによって実装されます。ただし、その概念はまだドメインの一部であり、このインターフェースによって表されます。

インフラ

外部依存関係の実装は、アプリケーションのインフラストラクチャの一部です。

私の解決策
+ My.Product.Core(My.Product.dll)
- My.Product.Infrastructure(My.Product.Infrastructure.dll)
  -DomainServices
      XEExchangeRateService
    SqlServerProductRepository

XEExchangeRateServicexe.comとIExchangeRateService通信してドメインサービスを実装します。この実装は、インフラストラクチャアセンブリを含めることにより、ドメインモデルを利用するアプリケーションで使用できます。

応用

アプリケーションサービスについてはまだ触れていません。今から見ていきます。IExchangeRateService高速な検索のためにキャッシュを使用する実装を提供したいとしましょう。このデコレータクラスの概要は次のようになります。

public class CachingExchangeRateService : IExchangeRateService
{
    private IExchangeRateService service;
    private ICache cache;

    public CachingExchangeRateService(IExchangeRateService service, ICache cache)
    {
        this.service = service;
        this.cache = cache;
    }

    // Implementation that utilizes the provided service and cache.
}

ICacheパラメータに注意してください。このコンセプトはドメインの一部ではないため、ドメインサービスではありません。これはアプリケーションサービスです。これは、アプリケーションによって提供される可能性があるインフラストラクチャの依存関係です。これを示すアプリケーションを紹介しましょう:

私の解決策
- My.Product.Core(My.Product.dll)
  -DomainServices
      IExchangeRateService
    製品
    ProductFactory
    IProductRepository
- My.Product.Infrastructure(My.Product.Infrastructure.dll)
  -ApplicationServices
      ICache
  -DomainServices
      CachingExchangeRateService
      XEExchangeRateService
    SqlServerProductRepository
- My.Product.WcfService(My.Product.WcfService.dll)
  -ApplicationServices
      MemcachedCache
    IMyWcfService.cs
  + MyWcfService.svc
  + Web.config

これはすべて、次のようなアプリケーションに統合されます。

// Set up all the dependencies and register them in the IoC container.
var service = new XEExchangeRateService();
var cache = new MemcachedCache();
var cachingService = new CachingExchangeRateService(service, cache);

ServiceLocator.For<IExchangeRateService>().Use(cachingService);

概要

完全なアプリケーションは、3つの主要な層で構成されています。

  • ドメイン
  • インフラ
  • 応用

ドメイン層には、ドメインエンティティとスタンドアロンドメインサービスが含まれます。外部リソースに依存するドメインの概念(ドメインサービスだけでなくリポジトリも含まれます)は、インターフェースによって定義されます。

インフラストラクチャ層には、ドメイン層からのインターフェースの実装が含まれています。これらの実装では、アプリケーションに提供する必要がある新しい非ドメイン依存関係が導入される場合があります。これらはアプリケーションサービスであり、インターフェイスによって表されます。

アプリケーション層には、アプリケーションサービスの実装が含まれます。インフラストラクチャ層によって提供される実装が十分でない場合は、アプリケーション層にドメインインターフェイスの追加の実装が含まれる場合もあります。

この視点は、サービスの一般的なDDD定義と一致しない場合がありますが、アプリケーションからドメインを分離し、複数のアプリケーション間でドメイン(およびインフラストラクチャ)アセンブリを共有できるようにします。


2
@ dario-g:リクエストモデルからドメインモデルを再構築/再入力し、ドメインモデルをドメインサービスに渡す必要があります。この質問はあなたにいくつかのアイデアを提供するかもしれません。そうでない場合は、お知らせください。他の質問に回答を追加する時間があるかどうかを確認します。
Niels van der Rest、

1
@Tiendq:IExchangeRateServiceインターフェースのことですか?これはドメインの概念です。つまり、顧客のユビキタス言語に含まれているものです。ドメインの他の部分はこのサービスに依存している可能性があります。そのため、そのインターフェースはドメイン層で定義されています。ただし、その実装には外部Webサービスが含まれるため、実装クラスはインフラストラクチャ層に存在します。このように、ドメイン層はビジネスロジックのみに関係しています。
Niels van der Rest

4
@Tiendq:従来の階層化アーキテクチャでは、インフラストラクチャは通常ドメインに依存しません。しかし、Onion Architecture(私の回答のリンクを参照)では、インフラストラクチャはドメインの外部依存関係を実装します。しかし、インフラストラクチャがドメインに依存しているとは言いません。参照するだけです。私はオニオンアーキテクチャから「インフラストラクチャ」という用語を採用しましたが、「外部」という名前の方がいいかもしれません。
Niels van der Rest

1
@Derek:それらの「もの」の1つはExchangeRateインスタンスである可能性があります。これには、基本通貨、カウンター通貨、およびこれら2つの通貨間の為替レート値が含まれます。これらの密接に関連する値は、ドメインからの「為替レート」の概念を表すため、これらはドメインレイヤーに存在します。単純なDTOのよ​​うに見えますが、DDDでは値オブジェクトと呼ばれ、インスタンスを比較または変換するための追加のビジネスロジックを含めることができます。
Niels van der Rest

6
私はあなたがビジェイに同意しない部分に同意しません、そしてここに理由があります。CachingExchangeRateServiceはインフラストラクチャの問題です。ICacheを一般的に受け入れている場合でも、そのICacheの実装は、関連するテクノロジ(Web、Windowsなど)によって異なります。それがジェネリックだからといって、それをアプリケーションサービスにするわけではありません。アプリケーションサービスは、ドメインのAPIです。アプリを作成している他の人にドメインを公開したい場合、彼らは何を使用しますか?アプリケーションサービス、およびそれらはキャッシュを必要としない可能性があるため、キャッシュ実装はそれらにとって役に立たない(つまり、インフラストラクチャである理由)
Aaron Hawkins

38

アプリケーションサービスとドメインサービスの違いを理解するのに役立つ最高のリソースは、ここにあるエリックエバンスの貨物サンプルのJava実装でし。ダウンロードすると、RoutingService(ドメインサービス)とBookingService、CargoInspectionService(アプリケーションサービス)の内部をチェックアウトできます。

私の「あは」の瞬間は2つのことによって引き起こされました。

  • 上記のリンクのサービスの説明、より正確にはこの文を読む:

    ドメインサービスは、ユビキタス言語とドメインタイプで表現されます。つまり、メソッドの引数と戻り値は適切なドメインクラスです。

  • このブログ記事、特にこの部分を読む:

    リンゴをオレンジから分離するのに大きな助けとなるのは、アプリケーションワークフローの観点から考えています。アプリケーションワークフローに関するすべてのロジックは通常、アプリケーションレイヤーに組み込まれるアプリケーションサービスになりますが、モデルオブジェクトとして適合しないように見えるドメインの概念は、1つ以上のドメインサービスを形成します。


3
これは私がアプリケーションサービスを定義する方法であり、これまでに遭遇したすべての状況に適合します。ドメインサービスは、ドメインオブジェクトに関連するすべてのものを扱いますが、それは単一のエンティティの範囲を超えています。例:BookReferencesService.GetNextAvailableUniqueTrackingNumber()、焦点は明らかにビジネスルール*です。アプリケーションサービスに関しては、まさにそれがあなたが説明していることです。ほとんどの場合、このビジネスワークフローをコントローラーアクションに組み込むことから始め、それに気付いたときに、アプリケーションサービスレイヤーでこのロジックをリファクタリングします。このレイヤーはユースケース用と言えるかもしれません
tobiak777

1
*そしてそのようなドメインサービスインターフェイスはドメインエンティティによって消費されます。
tobiak777 2015

32

ドメインサービスは、ドメインの拡張です。ドメインのコンテキストでのみ表示されます。これは、たとえばアカウントの閉鎖などのユーザーアクションではありません。ドメインサービスは、状態がない場所に適合します。それ以外の場合は、ドメインオブジェクトになります。ドメインサービスは、他の共同作業者(ドメインオブジェクトまたは他のサービス)で行われる場合にのみ意味のあることを行います。そしてその意味は別のレイヤーの責任です。

アプリケーションサービスは、ドメインオブジェクトとサービス間の相互作用を初期化および監視する層です。フローは通常、次のようなものです。リポジトリからドメインオブジェクト(またはオブジェクト)を取得し、アクションを実行して、そこに(それらを)戻します(または戻さない)。より多くのことができます。たとえば、ドメインオブジェクトが存在するかどうかを確認し、それに応じて例外をスローできます。したがって、ドメインオブジェクトとサービスを操作することで、ユーザーはアプリケーションと対話できます(これはおそらく、その名前の由来です)。アプリケーションサービスは通常、考えられるすべてのユースケースを表す必要があります。おそらく、ドメインについて考える前にできる最善のことは、実際に何をしようとしているのかについてより良い洞察を提供するアプリケーションサービスインターフェイスを作成することです。このような知識があると、ドメインに集中できます。

リポジトリは一般的に言えばドメインサービスに注入できますが、これはかなりまれなシナリオです。ほとんどの場合それを行うのはアプリケーション層です。


10
「ドメインサービスは、状態がない場所に適合します。それ以外の場合、ドメインオブジェクトになります。」クリックしてもらいました。ありがとうございました。
Nick

32

Red Book(Vaughn Vernonによるドメイン駆動設計の実装)から、これは私が概念を理解する方法です:

ドメインオブジェクトエンティティおよび値オブジェクト)は、(サブ)ドメインに必要な動作をカプセル化し、自然で表現力豊かで理解しやすいものにします。

ドメインサービスは、単一のドメインオブジェクトに適合しないそのような動作をカプセル化します。例えば、融資ブックライブラリBookClient(対応してInventory変化)は、ドメインサービスからそうかもしれません。

アプリケーションサービス、ドメイン加えて必要な追加の懸念事項を含む、ユースケースのフローを処理します。外部クライアントが使用するために、APIを介してそのようなメソッドを公開することがよくあります。前の例を基にして、アプリケーションサービスは次のメソッドLendBookToClient(Guid bookGuid, Guid clientGuid)を公開する場合があります。

  • を取得しClientます。
  • 権限を確認します。(ドメインモデルをセキュリティやユーザー管理の問題から解放していることに注意してください。そのような汚染は多くの問題を引き起こす可能性があります。代わりに、アプリケーションサービスでこの技術要件を満たします。
  • を取得しBookます。
  • ドメインサービスを呼び出し(Clientおよびを渡すBook)、本をクライアントに貸す実際のドメインロジックを処理します。たとえば、本の入手可能性を確認することは間違いなくドメインロジックの一部だと思います。

アプリケーションサービスは、通常、非常に単純なフローである必要があります。複雑なアプリケーションサービスフローは、ドメインロジックがドメインの外にリークしたことを示していることがよくあります。

うまくいけばわかるように、ドメインモデルはこのように非常にクリーンなままであり、ドメインの専門家と簡単に理解および議論できます。これは、独自の実際のビジネス上の懸念のみが含まれているためです。アプリケーションフローは、他の一方で、あるにもそれはドメイン上の問題から解放されるため、管理がはるかに容易に、かつ簡潔、かつ簡単になります。


3
アプリケーションサービスは、依存関係が解決されるポイントでもあると言えます。その方法は、単一のフローであるユースケースであるため、使用する具体的な実装に関する情報に基づいた決定を行うことができます。データベーストランザクションもここに適合します。
Timo

10

ドメインサービス:単一のエンティティに実際には適合しない、またはリポジトリへのアクセスを必要とするメソッドは、ドメインサービス内に含まれます。ドメインサービスレイヤーには、独自のドメインロジックを含めることもでき、エンティティや値オブジェクトと同じくらいドメインモデルの一部です。

アプリケーションサービス:アプリケーションサービスは、ドメインモデルの上に配置され、アプリケーションアクティビティを調整する薄いレイヤーです。ビジネスロジックが含まれておらず、エンティティの状態を保持していません。ただし、ビジネスワークフロートランザクションの状態を保存できます。アプリケーションサービスを使用して、Request-Replyメッセージングパターンを使用するAPIをドメインモデルに提供します。

ミレット、C(2010)。プロフェッショナルASP.NETデザインパターン。ワイリー出版。92。


7

ドメインサービス:集約ルートの一部ではないビジネスロジックを表現するサービス。

  • 2つの集計があります:

    • Product 名前と価格が含まれています。
    • Purchase これには、購入日、その時点で注文された製品の数量と価格、および支払方法のリストが含まれています。
  • Checkout はこれら2つのモデルのいずれにも含まれておらず、ビジネスのコンセプトです。

  • Checkoutすべての製品を取得して合計金額を計算し、PaymentServiceインフラストラクチャの実装部分を使用して別のドメインサービスを呼び出して合計を支払い、それをに変換するドメインサービスとして作成できますPurchase

アプリケーションサービス:ドメインメソッドを「オーケストレーション」または実行するサービス。これは、コントローラーと同じくらい簡単です。

これは通常行う場所です。

public String createProduct(...some attributes) {
  if (productRepo.getByName(name) != null) {
    throw new Exception();
  }

  productId = productRepository.nextIdentity();

  product = new Product(productId, ...some attributes);

  productRepository.save(product);

  return productId.value();
  // or Product itself
  // or just void if you dont care about result
}

public void renameProduct(productId, newName) {
  product = productRepo.getById(productId);

  product.rename(newName);

  productRepo.save(product);
}

Productが一意であるかどうかを確認するなど、ここで検証を行うことができます。Product一意であることは不変でない限り、クラスのUniqueProductChecker一部にすることはできずProduct、複数の集合体と相互作用するため、呼び出されるドメインサービスの一部である必要があります。

DDDプロジェクトの本格的な例を次に示します。https//github.com/VaughnVernon/IDDD_Samples

Application Serviceの例といくつかのDomain Serviceの例を見つけることができます


Application Servicesでのみエンティティを検証および保存することは必須ですか?エンティティA、B、Cがあり、それらすべてが相互に関連している場合(A-> B-> C)、Aでの操作により、あるドメインサービスを別のドメインサービスから呼び出すことにより、BとCに変更が生じるはずです。
MrNVK

>エンティティの検証と保存はアプリケーションサービスでのみ必須ですか?あなたがする必要がある場合は、はい。ほとんどの場合、IDが存在するかどうかを確認する必要があります。それ以外の場合は、null変数で作業するためです。
doesnotmatter

1
>エンティティA、B、およびCがあり、それらすべてが互いに関連している場合(A-> B-> C)、Aでの操作により、あるドメインサービスを別のドメインサービスから呼び出すことにより、BおよびCに変更を加える必要があります。その方法?「あるドメインサービスを別のドメインサービスから呼び出す」とはどういう意味かわかりませんが、エンティティの変更に対する反応については、イベントを使用するか、aggregateA.doOperation()、aggregateB.doAnother( )。検索:オーケストレーションvsコレオグラフィー
doesnotmatter 19/07/17

返信ありがとうございます!「あるドメインサービスを別のドメインサービスから呼び出す」-エンティティAで複雑な操作を行う場合は、ADomainServiceを使用する必要があります。ただし、この操作は、エンティティAに加えて、エンティティBに影響します。ADomainServiceのエンティティBで実行する必要がある操作も複雑です。したがって、ADomainServiceからBDomainServiceを使用する必要があります。今、私はこのアプローチを疑っています:)しかし、このロジックをApplicationServiceに配置すると、アプリケーションレイヤーではなく、ドメインレイヤーのみにあるべきビジネスプロセスのカプセル化が破られるのではないでしょうか。
MrNVK

アプリケーションサービスではなくドメインサービスにあると思われる場合は、ドメインサービスからイベントを発行できます。
doesnotmatter

1

ドメインサービスを、ビジネスロジックまたはビジネスルール関連のロジックをドメインオブジェクトに実装するオブジェクトと考えてください。このロジックは、同じドメインオブジェクトに適合させることが難しく、ドメインサービスの状態変更を引き起こしません(ドメインサービスは、ビジネス上の意味を持つ状態がない「状態」以上)が、最終的には操作対象のドメインオブジェクトの状態のみを変更します。

一方でアプリケーションサービスの認証、セキュリティ、電子メールで送信する、というように..単にドメインオブジェクトによって公開されるサービスを使用するために自分自身を制限:ユーザーとの対話、入力検証、ロジック事業ではなく、他の懸念に関連していないとして実装応用的レベルのロジック。

この例としては、目的を説明するためだけに考えられた次のシナリオが考えられます。「誰かが家の部屋のドアを開いて入るときに、ライトをオンにする」という単純な操作を実行する非常に小さなdomoticユーティリティアプリを実装する必要があります部屋から出るドアを閉めると、ライトが消えます。」

単純化するのは2つのドメインエンティティだけです。DoorそしてLamp、それぞれに2つの状態があり、それぞれopen/closedon/off、およびそれらの状態の変更を操作する特定のメソッドがあります。

この場合、ドアとランプオブジェクトが適切と考える方法でこのロジックを実装できないため、誰かがドアを外側から開いて部屋に入るときに、ライトをオンにする特定の操作を実行するドメインサービスが必要です。その性質に

ドメインサービスをとして呼び出し、DomoticDomainService2つのメソッドを実装できます。OpenTheDoorAndTurnOnTheLightそしてCloseTheDoorAndTurnOffTheLight、これらの2つのメソッドは、オブジェクトDoorLampto open/onおよびの両方の状態をそれぞれ変更しますclosed/off

部屋への入室または退室の状態は、ドメインサービスオブジェクトにもドメインオブジェクトにも存在しませんが、アプリケーションサービスによって単純なユーザー操作として実装されHouseServiceます。onOpenRoom1DoorToEnter、、などonCloseRoom1DoorToExit、各部屋(これは目的を説明するための例にすぎません。)は、それぞれ、呼び出しドメインサービスメソッドが関与する動作を実行することに関係しますRoom例にすぎないため、エンティティは考慮していません)。

この例は、十分に設計された実際のアプリケーションとは言えませんが、ドメインサービスとは何か、アプリケーションサービスとの違いを説明することだけを目的としています(何度も言っています)。


Ciro:あなたの例は実用的ではなく、非常に混乱しています。
Morteza Azizi

こんにちは、モルテザです。具体的に教えていただけますか?あなたの本当の議論のない単なる「判断」になるリスクがあります。ありがとう
Ciro Corvino
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.