RE: Re: Validation in CQRS Journey Conference project

Mauro Servienti <[email protected]>
Newsgroups gmane.comp.programming.domain-driven-design
Message-ID <0D71C042265E7B4D871B59569C7774D75A4C261E@AMXPRD0310MB390.eurprd03.prod.outlook.com>
What you are describing is a workflow/saga, that is async, and works pretty fine in your scenario.
The only scenario where things get complicated is when, as you noticed, validation involves more BCs.
In this case is the entry point BC that can take a decision:

-          Create Conference: Is there already a conflicting create conference process?

o   Yes: fail immediately;

o   No: start the creation process;

?  ValidateRequest;

?  ....and so on;

.m

From: [email protected] [mailto:[email protected]] On Behalf Of wonderfulworldofpingpong
Sent: domenica 2 giugno 2013 16.16
To: [email protected]
Subject: [domaindrivendesign] Re: Validation in CQRS Journey Conference project





Since I did not receive a response for a while, I thought to shed some light on the issue with this implementation.

The problem I see with this implementation is that the CreateConference does not log the CreateConference event. Consider the situation where the user has deleted the Conference having the same slug he created already. Now the user has to come back and do the CreateConference again with the same information he tried to enter in his first attempt to create it. He failed at that time because the same slug existed at that time. If the CreateConference event was stored, and is sourced from the EventStore again and replayed, the user does not have to enter the same data again. With this approach, I'm looking at the following commands and events.

  *   RequestConferenceCreationCommand - always wins
  *   RequestConferenceCreationEvent - always wins
  *   ConferenceValidatedEvent - Win or Fail based on the validation in other Bounded Contexts
  *   ConferenceCreatedEvent - Win or Fail

The RequestConferenceCreationCommand sends a RequestConferenceCreationEvent. This event is processed by the Bounded Contexts that can validate the business rules. When all those Bounded Contexts pass the validation, they send ConferenceValidateEvent. In this validation success event, the persistence of the conference object happens. Then the ConferenceCreatedEvent is broadcasted.

One problem I see with my approach is the events synchronizing from multiple Bounded Contexts to decided on all the Business Rules are passed. In this situation one Bounded Context which is listening from multiple Bounded Contexts that are doing the validation.

Let me know what you think.

--- In [email protected]<mailto:[email protected]>, "wonderfulworldofpingpong" wrote:
>
>
> The following code (in yellow background) is taken from the CQRS Journey
> project . In
> this code, a conference can be created in the Conference Bounded
> Context. No CQRS is being used here but simple CRUD.
>
> You can see a validation happening in this snippet of code in the
> CreateConference which is part of the Conference Service. If there is
> already an existing slug, the method throws an exception. This is a
> simple validation, so doing a validation at the service level may be
> okay. But we can make an argument that the validation has now leaked to
> the service.
>
> In real applications, in the place of checking for duplicate slug, there
> would be complicated validation logic. Also, the Conference BC may have
> to talk to other Bounded Contexts to decide on whether the conference
> can be created.
>
> My question is, how do we handle the creation of a conference with
> validation by talking to other Bounded Contexts? (1) Is it okay for the
> following method / command wait until the other BC returns with the
> validation status? The operation is synchronous. If the validation from
> the other Bounded Context is true, then continue creating the
> conference. (2) Instead of making this as synchronous, the Conference
> service in this method / command, send a validation command to the other
> Bounded Context. Then in the HandleEvent, create the conference. This
> approach can scale.
>
> public void CreateConference(ConferenceInfo conference)
> {
> using (var context = new
> ConferenceContext(this.nameOrConnectionString))
> {
> var existingSlug = this.retryPolicy.ExecuteAction(() =>
> context.Conferences
> .Where(c => c.Slug == conference.Slug)
> .Select(c => c.Slug)
> .Any());
>
> if (existingSlug)
> throw new DuplicateNameException("The chosen
> conference slug is already taken.");
>
> // Conference publishing is explicit.
> if (conference.IsPublished)
> conference.IsPublished = false;
>
> context.Conferences.Add(conference);
> this.retryPolicy.ExecuteAction(() =>
> context.SaveChanges());
>
>
> this.PublishConferenceEvent (conference);
> }
> }
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.