私は少し混乱しています
私の理解が正しいなら
Product
集約ルートがあります。製品には、複数のアクティブな特別オファーがある場合があります。 SpecialOffer
sを管理するために、製品は2つのドメインイベントを受け入れます。
そのため、パブリックインターフェイスは、2つのオーバーロードされたApply
メソッドです。
class Product{
...
Apply(SpecialOfferActivated){...}
Apply(SpecialOfferDeactivated){...}
}
したがって、Controller
は行の最初にあります。基本的には、発信者の意図をデータコントラクト(DTO)からドメイン言語(コマンド)に変換します。
class ProductController{
Post(SpecialOfferDto dto){
ActivateSpecialOfferCommand command = Map(dto)
_commandBus.Send(command)
}
}
コマンドが送信されました。今度はコマンドハンドラが必要です
class ActivateSpecialOfferCommandHandler{
Handle(ActivateSpecialOfferCommand command){
SpecialOfferActivated domainEvent = Map(command)
_eventBus.Publish(domainEvent)
}
}
イベントが公開されました。今度はイベントハンドラの時間です
class SpecialOfferActivatedDomainEventHandler{
Handle(SpecialOfferActivated domainEvent){
var product = GetFromDatabase()
product.Apply(domainEvent)
Save(product)
}
}
できました。
今回はNewPromotionExternalEvent
がデータコントラクト(ExternalEvent
)であり、ドメイン言語(Command
)に翻訳する必要があります。
class NewPromotionExternalEventHandler{
Handle(NewPromotionExternalEvent extenalEvent){
ActivateSpecialOfferCommand command = Map(extenalEvent)
_commandBus.Send(command)
}
}
そして、最初のケースからActivateSpecialOfferCommandHandler
にフォールバックします。つまり、基本的には最初のケースと同じです。
したがって、APIまたは外部イベントのいずれかによってコマンドが生成されました。集約ルートに適用するために、ドメインイベントを作成するだけです。 notイベントをサービスバスに発行します。
class ActivateSpecialOfferCommandHandler{
Handle(ActivateSpecialOfferCommand command){
SpecialOfferActivated domainEvent = Map(command)
var product = GetFromDatabase()
product.Apply(domainEvent )
Save(product)
}
}
できました。
コマンドレイヤーは簡単にスキップできます
class ProductController{
Post(SpecialOfferDto dto){
SpecialOfferActivated domainEvent = Map(dto)
_eventBus.Publish(domainEvent)
}
}
SpecialOfferActivatedDomainEventHandler
にフォールバック
したがって、APIまたは外部イベントによって、コマンドCreateNewProductCommand
が生成されました。そして、別のハンドラが必要です:
CreateNewProductCommandHandler{
Handle(CreateNewProductCommand command){
var product = Map(command)
SaveToDatabase(product)
NewProductCreated domainEvent = Map(product)
_eventBus.Publish(domainEvent) // in case somebody is interested
}
}
この場合、ドメインイベントレイヤーを貼り付ける場所は本当にありません。
class Product{
Apply(SpecialOfferActivated domainEvent){
var specialOffer = Map(domainEvent)
_specialOffers.Add(specialOffer)
if(...){
// For simplicity sake, assume the aggregate root can access _eventBus
_eventBus.Publish(new ProductReceivedTooManySpromotionsDomainEvent(this.Id))
}
}
}
NewPromotionExternalEvent
は本当に外部イベントですか、それともドメインイベントですか?私は少し混乱しています
それはあなたのせいではありません。文学はひどい。
人々が実際に話しているのは、情報が「ドメインモデル」の周りを流れる方法です。
これらの最初と最後は本当にmessagingについてです。
しかし、「ドメインイベント」のスペルはごちゃごちゃのようなものです。これらは、どちらもフロアワックスであり、デザートのトッピングです。 「イベントソーシング」のコンテキスト内では、それらは本当にpersistenceについてです。ただし、「ドメインイベント」という語句は、同じモデル内のアグリゲート間で情報を共有するために使用されるメッセージを説明するためにも使用されます。
コマンドレイヤーのポイントは何ですか?
ほとんどの場合、コントローラの懸念からアプリケーションの懸念(メッセージが正しい集約に配信され、トランザクションを処理することを保証する)を切り離す方法を提供します。また、ドメインモデルが機能する前に、特定の横断的関心事(タイミングなど)を適用する簡単な方法も提供します。
公開せずに、集約ルートに適用するためだけにドメインイベントを作成する
はい、しかし私はそれをいくぶん異なる綴りで書くでしょう-私たちは通常、集約の外でcreateイベントを行いません。アグリゲートは、状態の変更方法を決定するビジネスロジックを所有します(データのダムバッグではありません)。しかし、それが起こったことを世界に通知することなく、集合体が状態を変更することは完全に正常です。
外部イベントとドメインイベントの境界はどこですか?
通常、「外部イベント」はドメインイベントよりもはるかに薄いです。通常、サービスの個人情報をどこにでも共有したくないので、通常、内部実装の詳細のすべてを公開することはありません。また、外部イベントはサービス間の契約の一部です-その契約を変更するための戦略は、より多くの利害関係者を満足させる必要があります。ただし、単一のサービス内で維持されるデータは、変更にかかるコストが低くなります。
ドメインイベントを作成できるのは誰ですか?
集約ルート。理論的には、他のエンティティも同じ集合体に含まれています。
リクエストがコントローラーまたは外部イベントから来たときに、製品の作成と特別オファーのアクティブ化を処理する適切な方法は何ですか?
入ってくる情報->ドメインモデルの適切な部分をメモリにロードし、それに情報を渡して、変更を計算できるようにします。
集約ルートが唯一のドメインイベントを生成する場合、問題が発生します。集合体と対話する(そしてそれが独自のドメインイベントを生成する)唯一の方法は、ドメインイベントをそれに適用することです。
通常、集約ルートと対話する方法は、コマンドを介して集約ルートに情報を送信することです。
たとえば、 DDDサンプルアプリ を見てください。特に、ドメインモデル( ここ および ここ )に対して アプリケーションレイヤー がビジネスロジックをどのように実行していないかに注意してください。
また、ドメインイベントは、同じドメインモデル内の他のアグリゲートと通信するのに役立つと述べました。ドメインモデルは単一のマイクロサービスに限定されていないと思います。
歴史的に、ドメインイベントは、同じトランザクションに参加している他のアグリゲートと通信するために使用されていました。つまり、同じトランザクションに参加している場合、それらは同じマイクロサービスの一部です。
トランザクション境界を越えて情報を共有する場合、トランザクション内で情報を共有する場合とは設計上の考慮事項が異なります。
私は最近、人々がマイクロサービスの境界を境界のあるコンテキストに揃えていることを聞く可能性が高くなると思います。つまり、モデルは共有されません(理にかなっているはずです-マイクロサービスのポイントの一部は、独立して進化することができます)。