Updating UI after sending command
Asked Answered
K

2

9

I'm struggling in solving this architectural problem, Our system is using NService Bus, and Implementing DDD with EventSourcing using NEventStore and NES. The client application is WPF

I still can't decide what is the best practice to update the UI, for example: I have a UI to create (Batch {Id, StartDate, EndDate ,etc..}), after the user clicks save, I'm sending a command (CreateBatch),

Bus.Send<CreateBatch> (batch => {batch.Id = Guid.NewGuid(); .... etc });

Now,

Option #1


Shall I register for a reply as follows:

private void btnSave_Click(object sender,EventsArg e){
    Bus.Send<CreateBatch> (batch => {batch.Id = Guid.NewGuid(); .... etc })
       .Register<int>(c=> { 
                     MessageBox.Show("Command Succeded");
                     Close();});
}

and at server side:

public void Hanlde(CreateBatch cmd){
   //initiate aggregate and save it.
   Bus.Return( /*What ?? Success Code?*/);
}

In this case, how to handle errors? (Validation errors, for example there is already a batch starts in the same date ?), since you can only return int or string !!

Option#2:


Send the command, close the window, fake that the record is added in the main grid, until user refresh the grid and get the real one from ReadModel database, or discover that the record is not added yet with no clue about what happened !!

private void btnSave_Click(object sender,EventsArg e){
    Bus.Send<CreateBatch> (batch => {batch.Id = Guid.NewGuid(); .... etc });
    Close();
}

Option#3


Send the command, close the window, fake that the record is added in the main grid but mark it as (In progress), wait for (BatchCreated) event to arrive, check that it is the same batch id we sent before, and then mark the record as persisted in the grid.

private void btnSave_Click(object sender,EventsArg e){
    Bus.Send<CreateBatch> (batch => {batch.Id = Guid.NewGuid(); .... etc });
    Close();
}


public class BatchHandler : IHandleMessages<BatchCreated>
{
    public void Handle(BatchCreated evnt)
    {
     if(SomeCachForSentIDS.Contains(evnt.BatchId)
         ///Code to refresh the row in the grid and reflect that the command succeeded.
    }
}

If you have any better options, or have suggestions about the mentioned options, or even some links about how to handle such situation, I would be grateful.

Thank you.

Knobloch answered 27/5, 2014 at 10:37 Comment(4)
Actually, it depends in which sense they are not acceptable !,in sense of fooling the user, I have seen a lot of people (including Udi Dahan) mentioning option 2 as good one, they called it (Trick the user) !. For option 3, I still cannot tell whether to use or not !Knobloch
Its not tricking if you (like Udi says) set your UI to the expected state. Usually the Command should hardly fail.Mentally
Another thing to think about is do you need to take them immediately to a grid view of the data? If this this part of some workflow you could go to the next step and avoid the grid.Welcome
@AdamFyles let say I can avoid the grid, what if an error ocurred, and the command didn't succeed? what if the user is waiting for the batch to be created, in order to start fill it ?Knobloch
L
7

One important principle when introducing messaging into a solution like this is to make sure that the command will hardly ever fail (as @Gope mentioned in the comments).

This means that all sorts of validation checks should be performed client-side before sending the message.

Now, even when doing client-side validation, you can still be exposed to race conditions - 2 users creating a batch at the same time that conflict with each other in some way. The way to handle this is to look to clarify the user intent, possibly changing the nature of the task as well as the UI, to make it so that the new command will succeed even in the case of the parallel processing.

Just as a simple stab at a solution, you could redefine the logic to introduce a new state for a batch - like "conflicting", that will be set if another batch starts at the same time. This way the command succeeds (the batch is created), and then you use something like SignalR to push back a notification of this state to the user so they can go about correcting it.

Finally, you might want to reconsider the use of messaging entirely and just go to a simple 2-tier synchronous solution. There's a time and place for every approach.

Lh answered 27/5, 2014 at 19:13 Comment(0)
C
1

How to design these user interfaces for asynchronous messaging (especially after we've been indoctrinated into the CRUD mindset) is the million dollar question in each of these systems.

I agree with Udi's assessment 100%. Additionally, your question seems pretty opaque to the exact business requirements, but the CRUD nature of the CreateBatch command and BatchCreated event's names make me wonder if it's really a useful thing to do with messaging at all.

If you find yourself in a situation where you have to wait for CreateBatch to complete so you can do more things to it, maybe you shouldn't have a CreateBatch at all. Maybe you should just be creating something "locally" (that could mean client-side in JavaScript, or as Udi suggests, in the simplest 2-tier database model possible) and then send a command to do the real work (ProcessBatch perhaps?) once everything is complete.

The goal of messaging, after all, is not to completely divorce the web tier from making requests of the database.

Cuff answered 28/5, 2014 at 4:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.