I've read an interesting blog post today about custom exceptions (here - unfortunately it's in Polish), where the author advocates for using "business exceptions".
The example comes from a dialling application, so there are the following exceptions:
- CallAlreadyInProgressException,
- IncorrectNumberException,
- NumberAlreadyDialedException
When I first looked at the code, I nodded in agreement. Given such domain, those exceptions make total sense to me. I'd implement it the same way.
When I linked to this post in our team chat, Mirek (who has more knowledge about DDD and CQRS than I do) said that it'd be better to implement with domain events. This surprised me a bit, as when the topic of custom exceptions is brought up, domain events are rarely shown as an alternative.
What's the difference between custom exceptions and domain events?
Look at this code:
Domain events decouple the call to the method from returning the result. This is at the core of the CQRS approach - one of the most inspiring architectures I've ever read about. In short - any system operation is either a Command (a change to the system) or a Query (a read from the system). This rule drives the whole architecture.
In the code, it means that when you call dialler.call(number), you're not directly interested in the result, even to the point, that the method can fail (raise an exception). You made a command and that's it.
Now, obviously, you do need to know about the failure. That's why you publish an event, using whatever mechanism under the hood. It can be a simple singleton EventBus class, which just keeps a map of objects interested in certain events and notifies them if any such event happens. In our case, we could have a UINotifier class listening to the CallAlreadyInProgress event and sending a special UI message to the user of the system (the technical details are not important here - it can be via polling or Web Sockets).
There is another difference - with events we need to "publish" the fact that all went fine. What was implicit with exceptions (no exception is raised - success), here needs to be explicit. We publish the "ConnectionEstablished" event.
This creates a nice simplification of the whole code around it. It may make it a bit more indirect, but it's actually very simple. All of the pieces of code involved do just one thing.
7 comments:
In the 2nd to last paragraph, you say: "What was implicit with exceptions (no exception is raised - success), here needs to be implicit. We publish the "ConnectionEstablished" event."
Should it be "here needs to be EXPLICIT"?
Thanks! Fixed.
Hi Andrzej,
Thanks for the concise yet informative post.
I've been using Domain Events in my projects (albeit C#) as well. However I've had issues when I have to deal with Business states that arise as a result of a combination of domain events pertaining to multiple domain objects (E.g If a call_is_already_in_progress and the customer_has_voice_mail leave a message to call back). I use to use a service layer to orchestrate these kind of scenarios but was never happy. Interested to know how you guys would solve a similar problem.
For more info check out the question at the bottom of my blog post at http://www.uchithar.net/2013/03/simplifying-design-with-domain-events.html.
Again thanks for the post.
Hi Uchitha.
How about a Choreographed Process as shown on slide 95 of http://www.slideshare.net/jeppec/soa-and-event-driven-architecture-soa-20 ?
Maybe you just need a piece of infrastructure that listens to both kinds of events and transforms them into a new event? I am not sure if this is exactly your scenario in hope this might be useful to you.
Good luck.
So instead of exceptions, the two sections of code communicate via a global variable?
So what happens when you have those lines
dialer.dial(number)
someOther.Code()
and dialer.dial(number) publishes error event? In case of exceptions code after dialing will not be run. In case of Domain events it wll and that is wrong.
I am working on a project where original authors used similiar approach. After 2 years it's really messy because you cannot follow program flow due to proliferation of events that cause other events which in turn cause other events which cause other events... and so on.
Having this experience I would apply KISS to this kind of problems in first place and just go with custom exceptions. Events only when you have to be asynchronous.
Post a Comment