web-dev-qa-db-ja.com

アプリケーションまたはドメインサービスのDDDリポジトリ

最近DDDを勉強しています。DDDでリポジトリを管理する方法について質問があります。

実際、私は2つの可能性に出会いました。

最初のもの

私が読んだサービスを管理する最初の方法は、リポジトリとドメインモデルをアプリケーションサービスに挿入することです。

このように、アプリケーションサービスメソッドの1つで、ドメインサービスメソッドを呼び出し(ビジネスルールをチェック)、状態が良好であれば、データベースからエンティティを永続化/取得するための特別なメソッドでリポジトリが呼び出されます。

これを行う簡単な方法は次のとおりです。

class ApplicationService{

  constructor(domainService, repository){
    this.domainService = domainService
    this.repository = repository
  }

  postAction(data){
    if(this.domainService.validateRules(data)){
      this.repository.persist(new Entity(data.name, data.surname))
    }
    // ...
  }

}

2番目

2番目の可能性は、代わりにdomainService内にリポジトリを挿入し、ドメインサービスを介してのみリポジトリを使用することです。

class ApplicationService{

  constructor(domainService){
    this.domainService = domainService
  }

  postAction(data){
    if(this.domainService.persist(data)){
      console.log('all is good')
    }
    // ...
  }

}

class DomainService{

  constructor(repository){
    this.repository = repository
  }

  persist(data){
    if(this.validateRules(data)){
      this.repository.save(new Entity(data.name))
    }
  }

  validateRules(data){
    // returns a rule matching
  }

}

これからは、どちらが最適か(ある場合)、またはどちらがコンテキストで意味するかを区別できなくなります。

どちらがより優れているか、そしてその理由を教えてください。

31
mfrachet

簡単に言えば、アプリケーションサービスまたはドメインサービスからリポジトリを使用できますが、その理由と方法を検討することが重要です。

ドメインサービスの目的

ドメインサービスは、ドメインの概念/ロジックをカプセル化する必要があります。つまり、ドメインサービスのメソッドは次のとおりです。

domainService.persist(data)

persistユビキタス言語の一部ではなく、永続化の操作はドメインビジネスロジックの一部ではないため、ドメインサービスに属していません。

一般に、ドメインサービスは、複数のアグリゲートを調整または操作する必要があるビジネスルール/ロジックがある場合に役立ちます。ロジックが1つの集約のみを含む場合は、その集約のエンティティのメソッドに含める必要があります。

アプリケーションサービスのリポジトリ

その意味で、あなたの例では、私はあなたの最初のオプションを好みます-しかし、ドメインサービスがAPIから生データを受け入れているため、改善の余地はあります-ドメインサービスがdata?。さらに、データは単一の集計にのみ関連しているように見えるため、そのためにドメインサービスを使用する価値は限られています。通常、検証はエンティティコンストラクター内に配置します。例えば.

postAction(data){

  Entity entity = new Entity(data.name, data.surname);

  this.repository.persist(entity);

  // ...
}

無効な場合は例外をスローします。アプリケーションフレームワークによっては、例外をキャッチし、それをAPIタイプの適切な応答にマッピングするための一貫したメカニズムを用意するのが簡単な場合があります。 REST apiの場合、400ステータスコードを返します。

ドメインサービスのリポジトリ

上記にかかわらず、ドメインサービスにリポジトリを挿入して使用すると便利な場合がありますが、リポジトリが集約ルートのみを受け入れて返すように実装されており、複数の集約を含むロジックを抽象化している場合に限られます。例えば.

postAction(data){

  this.domainService.doSomeBusinessProcess(data.name, data.surname, data.otherAggregateId);

  // ...
}

ドメインサービスの実装は次のようになります。

doSomeBusinessProcess(name, surname, otherAggregateId) {

  OtherEntity otherEntity = this.otherEntityRepository.get(otherAggregateId);

  Entity entity = this.entityFactory.create(name, surname);

  int calculationResult = this.someCalculationMethod(entity, otherEntity);

  entity.applyCalculationResultWithBusinessMeaningfulName(calculationResult);

  this.entityRepository.add(entity);

}

結論

ここで重要なのは、ドメインサービスencapsulatesがユビキタス言語の一部であるプロセスであることです。その役割を果たすためには、リポジトリを使用する必要があります-そうすることはまったく問題ありません。

ただし、persistというメソッドでリポジトリをラップするドメインサービスを追加しても、ほとんど価値はありません。

これに基づいて、アプリケーションサービスが単一のアグリゲートでの作業のみを必要とするユースケースを表現している場合、アプリケーションサービスから直接リポジトリを使用しても問題はありません。

34
Chris Simon

受け入れられた回答に問題があります:

ドメインモデルはリポジトリに依存することが許可されておらず、ドメインサービスはドメインモデルの一部です->ドメインサービスはリポジトリに依存しないでください

代わりに、アプリケーションサービスで既にビジネスロジックの実行に必要なすべてのエンティティをアセンブルし、モデルにインスタンス化されたオブジェクトを提供するだけです。

あなたの例に基づいて、それは次のようになるでしょう:

class ApplicationService{

  constructor(domainService, repository){
    this.domainService = domainService
    this.repositoryA = repositoryA
    this.repositoryB = repositoryB
    this.repositoryC = repositoryC
  }

  // any parsing and/or pre-business validation already happened in controller or whoever is a caller
  executeUserStory(data){
    const entityA = this.repositoryA.get(data.criterionForEntityA)
    const entityB = this.repositoryB.get(data.criterionForEntityB)

    if(this.domainService.validateSomeBusinessRules(entityA, entityB)){
      this.repositoryC.persist(new EntityC(entityA.name, entityB.surname))
    }
    // ...
  }
}

したがって、経験則:ドメインモデルは外層に依存しません

アプリケーションvsドメインサービスFrom この記事

  • アプリケーションサービスはAPIの提供を目的としたファサードであるため、ドメインサービスは非常に詳細です。

  • ドメインサービスには、エンティティまたは値オブジェクトに自然に配置できないドメインロジックが含まれていますが、アプリケーションサービスはドメインロジックの実行を調整し、それ自体はドメインロジックを実装しません。

  • ドメインサービスメソッドは、オペランドおよび戻り値として他のドメイン要素を持つことができますが、アプリケーションサービスは、ID値やプリミティブデータ構造などの自明なオペランドで動作します。

  • アプリケーションサービスは、ドメインロジックの実行に必要なインフラストラクチャサービスへの依存関係を宣言します。

4
sMs

サービスとオブジェクトが一貫した一連の責任をカプセル化しない限り、どちらのパターンも適切ではありません。

まず、ドメインオブジェクトとは何か、そしてドメイン言語内で何ができるかについて話します。それが有効または無効である可能性がある場合は、ドメインオブジェクト自体のプロパティとしてこれを持たないのはなぜですか?

たとえば、オブジェクトの有効性が別のオブジェクトに関してのみ意味をなす場合、サービスのセットにカプセル化できる「ドメインオブジェクトの検証ルールX」を担当している可能性があります。

オブジェクトを検証するには、ビジネスルール内にオブジェクトを保存する必要がありますか?おそらく違います。 「オブジェクトの保存」の責任は通常、別のリポジトリオブジェクトにあります。

これで、実行したい操作があり、さまざまな責任をカバーし、オブジェクトを作成し、検証して、有効な場合は保存します。

この操作はドメインオブジェクトに固有ですか?次に、それをそのオブジェクトの一部、つまりExamQuestion.Answer(string answer)にします

ドメインの他の部分に適合しますか?そこに置くBasket.Purchase(Order order)

代わりにADMを実行しますかREST services?Ok then。

Controller.Post(json) 
{ 
    parse(json); 
    verify(parsedStruct); 
    save(parsedStruct); 
    return 400;
}
1
Ewan