web-dev-qa-db-ja.com

DDD、インフラストラクチャサービスを使用する場所

私はDDDを学ぼうとしていますが、インフラストラクチャサービスの扱いに問題があります。

実際、私は彼らが何を目指しているのか理解していますが、私のアプリケーション内のどこに収まるのかわかりません。

実際、私は次のものを持っています

  • アプリケーションサービス(REST API)
  • 一部のドメインサービス(仕様パターンで管理)
  • 一部のインフラストラクチャサービス(リポジトリ、および電子メール送信者)

質問1使用するドメインサービスの役割(つまり、ドメインサービスに挿入されたインフラストラクチャサービス)か、(非常にシンプルな方法で)のようなものかと思っていました。

class UserDomainService {

  constructor (userRepository, emailService) {
    this.userRepository = userRepository
    this.emailService = emailService
  }

  saveUser (user) {
    /** Apply some business rules here ...*/
    const user = this.userRepository.save(user)
    this.emailService.send('New user added : ' + user.firstName)
  }

}

または、次のように、アプリケーションサービスの役割である必要があります。

class ApplicationService {

  constructor (userDomainService, userRepository, emailService) {
    this.userDomainService = userDomainService
    this.userRepository = userRepository
    this.emailService = emailService
  }

  postUser (user) {
    if (this.userDomainService.manageSomeRules(user)) {
      this.userRepository.save(user)
      this.emailService.send('New user added : ' + user.firstName)
      return user
    }
    return new RuleNotSatisfied()
  }

}

質問2

受信したユーザーをPOSTエンドポイント(REST API))から適切なエンティティに変換する責任は誰にありますか?

質問2の編集:エンティティをDTOに、またはその逆に変換できるサービスの種類を教えてください。どこに置けばいいですか?

6
mfrachet

質問1

インフラストラクチャサービスがドメインの概念(エンティティと値オブジェクト)を受け入れて返す場合、ドメインサービスがインフラストラクチャサービスを使用することは問題ありません。

さらに、ドメインサービスは、エンティティに適合できないビジネスロジックの一部をカプセル化する必要があります。したがって、saveUserはドメインサービスに適した方法ではありません。

尋ねる質問は-メールを送信することは重要なドメイン概念ですか? Emailはドメインのエンティティですか?その場合、ドメインレイヤーで定義された電子メール送信者用のインターフェースがあり、インフラストラクチャレイヤーに分離された実装がある可能性があります。 、そしてドメインサービスで次のことを有意義に行うことができます-:

_sendEmailIfWhiteListed(id) {
    Email = this.emailRepository.get(id);
    if (email.isWhiteListed) {
        this.emailSender.send(email);
    }
}
_

ただし、これは、メールがドメイン内のエンティティである場合にのみ有効です。そうでない場合は、考えてみてください-なぜ電子メールの送信をユーザーを保存する責任にするのですか?

あなたの場合、メールはあなたのユビキタス言語のエンティティではないようです。したがって、ここでは ドメインイベントパターン を優先します。新しいユーザーを作成すると、ドメインイベントNewUserCreatedが発生します。また、アプリケーションレイヤーに配置するドメインイベントのハンドラーは、メール送信者に委任してメールを送信します。

質問2

投稿データを意味のあるエンティティに変換する責任は誰にありますか?それはあなたが何をしているのかに依存します:

  1. 新しいユーザーを追加する場合
    1. 単純なロジックの場合、アプリケーションサービスはnew Entity()を呼び出し、api data引数からマップされた意味のあるドメイン引数(値オブジェクト)を渡すことができます
    2. 複雑なロジックの場合、ファクトリと呼ばれる特別なドメインサービスを使用できますthis.entityFactory.Create()そして、api data引数からマッピングされた意味のあるドメイン引数(値オブジェクト)を渡します
    3. どちらの場合も、エンティティまたはファクトリはdataの構造を認識してはなりません。これはAPIの問題であるためです。
  2. ユーザーを更新する場合
    1. アプリケーションサービスは、既存のユーザーを取得するためにリポジトリを使用する必要があります
    2. ユーザーは関連データを更新するためのメソッドを持っている必要があります。 しかし_user.update_のようなCRUDスタイルのメソッドは避けたいと思いますについて考えてくださいはユーザーデータが更新しました? DDDは、ビジネスプロセスと目的について考えることを奨励しています。
      1. 一部のビジネスプロセスに参加しているため、ユーザーは更新されていますか?次に、ビジネスプロセスを明示的にモデル化し、プロセスの副作用としてユーザーデータを変更します。
      2. プロファイルページの編集など、単純なCRUDインタラクションのためにユーザーが更新されていますか?それでも、_user.UpdateProfile_のようなエンティティメソッドで操作の目的を明示することをお勧めします。

DDDは言語に関するすべてです。ドメインの専門家が使用する言語を聞き、ユビキタス言語に同意し、コードで忠実に使用します。ドメインエキスパートがpersistまたはsaveを言わない場合は、ドメインレイヤーでそれらの単語を使用しないでください。

5
Chris Simon

最初の答えはDDDだと思います。ロジックをドメインオブジェクトに配置します。必要に応じてサービスを注入します。

着信メッセージをドメインオブジェクトに変換するための別の「ホスティングレイヤー」があります。

アプリケーション層は、ドメインオブジェクトを使用して必要な効果を実現します。

しかしながら。 DDDがサービスパターンにあまり適合しないと思います。私の考えでは、ロジックはドメインオブジェクトではなくサービスに属していることがよくあります。

つまりRecordPlayer.PlayRecord(record)ではなくRecord.Play()を使用します

さらに、これらのサービスの性質は、それらがステートレスであることです。私はレコードを再生し、結果を返し、すべてを破棄します。

一方、デスクトップアプリではDDDの方が優れています。いくつかのレコードをインスタンス化して、それらに対してさまざまな機能を実行する場合があります。棚から取得、再生、一時停止など。DDDスタイルのコードは、ユーザーのアクションと思考プロセスに一致します。 「このレコードを再生します」-> record.Play()

2
Ewan