CQRS-ES: How do you tell the world about aggregate errors?

1,522 views
Skip to first unread message

Werner Clausen

unread,
Nov 29, 2012, 6:39:57 AM11/29/12
to ddd...@googlegroups.com
Hi,
 
Perhaps I don't use the correct terms but found surprisingly little material when searching this subject. The scenario: An UI sends a command (CreateCustomer) to a Customer handler that detects that the crappy UI didn't validate the command correct - the customer name is empty. So this is the error situation - what to do about it? I see 3 options:
 
1. Serialize the faulty Customer anyways with the data at hand.
The UI will discover the mistake when looking at the customer later. Problem here is that the UI might never discover the faulty customer - well because the data isn't as expected. In general there could be a number of errors happening because of this faulty serialization and subsequent events with missing data.
 
2. Throw exception.
    a) How will the UI get notified about the error?
    b) If the handler was part of a Saga, do you have some admin tools that can close/invalidate/complete the saga? Or just using timeouts?
3. Publish ICustomerNotCreated event
Does this option imply that all command handler should have 2 exit states in respect to events?
 

Did I miss an option? How do you guys handle such scenarios - in general?
 
--
Werner
.
 
 

Greg Young

unread,
Nov 29, 2012, 6:55:17 AM11/29/12
to ddd...@googlegroups.com

raise an event about the error

or

return nak and error to client

Werner Clausen

unread,
Jan 8, 2013, 4:23:00 AM1/8/13
to ddd...@googlegroups.com
I guess there is no perfect way to handle such scenarios. I will take it to my team and we will go from there...Thanks for answering.
 
--
Werner

Phillip Cleveland

unread,
Jan 8, 2013, 9:50:02 AM1/8/13
to ddd...@googlegroups.com
Seems like throwing an exception from the domain object then handling that in the command handler would work nicely. I think you may want to add ACK/NAK to your command pipeline, so the UI can respond appropriately.

Sent from my iPad

Kasey Speakman

unread,
Jan 8, 2013, 5:42:33 PM1/8/13
to ddd...@googlegroups.com
I throw a specific exception (like a custom exception named DomainException), and let the message explain why the operation failed.

For synchronous commands, I return a simple object that has a bool for Pass/Fail and a message for the fail case. e.g.

{ Success: true } or
{ Success: false, Message: "You shall not pass!" }

Whatever calls the handler method wraps the call in try/catch like below.

var response = null;
try
{
    handler.Handle(command);
    response = new CommandResponse(); // success (ACK)
}
catch (DomainException ex) // only DomainExceptions handled for security reasons
{
    response = new CommandResponse(ex.Message); // failure (NACK)
}
return response;


That way non-domain exceptions are not exposed to the client.

Werner Clausen

unread,
Jan 9, 2013, 3:04:09 AM1/9/13
to ddd...@googlegroups.com
 
Yes that makes sense as long as we are talking about errors that we know the client must and can correct. Like my example that is merely a validation of the input. However, what if the error isn't something we expected, like an offline database. In my case, I want to outcome of that as an exception (transaction fails). I could send back a NAK under transaction suppression?
 
--
Werner

@neilbarnwell

unread,
Jan 9, 2013, 3:46:36 AM1/9/13
to ddd...@googlegroups.com
I'm using .NET so I've gone with using exceptions.

Specifically though, I've created a DomainException class (there are a couple of super-specific exceptions that inherit from it but generally it is good enough on it's own). However, because it's being raised by domain logic, it has a few extra properties. In addition to the Message (what happened/went wrong e.g. "Unable to place order"), it has the following:

// Why did the exception happen? (e.g. "Order has already been cancelled")
string Reason { get; } 

// What could the user do about it? (e.g. "Create a new order")
string[] Recommendations { get; }

// What could a software support person do? (Not surfaced in the UI, only logs. e.g. Configuration to check)
string[] AdvancedRecommendations { get; }

// A unique ID in case the exception is returned over WCF as a FaultException or sent as a message, so software support people can find the exception in client/server logs etc
Guid Id { get; }

These exceptions are thrown from within the Entities themselves (often when invariant checking fails), and bubble up the "stack". In the case of a webapp, they'd be an error dialog or page. In the case of a "smart client" they'd be converted to a FaultException<DomainFault> by using a custom WCF IErrorHandler and converted back and re-thrown on the client.

In addition, if it's useful, there's nothing stopping you sending a failure message (event?) onwards. Particularly if you're running as a downstream event processor - you can't really bubble exceptions backwards because the point is you're handling an event that HAS ALREADY HAPPENED. The always have to move forwards, even if that's notifying of a failure rather than a success.

HTH.

Kasey Speakman

unread,
Jan 9, 2013, 4:42:51 AM1/9/13
to ddd...@googlegroups.com
In these cases, I don't pass the exception back to the client. I let it fall through to the framework and assume that either a) it's something we've seen happen before and is going to be handled and returned as a sanitized system error (as opposed to a domain error) or b) it's something we've never seen and it needs to crash (which is a NACK) or at least get logged/NACKed with a generic message so I know there's a problem.
Reply all
Reply to author
Forward
0 new messages