Eventuous/dotnet-sample

Performance question: is a good idea use DomainExceptions

Closed this issue · 5 comments

We can see if (!roomAvailable) throw new DomainException("Room not available"); and I remember that guideline from .Net Framework Design Guidelines:
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/exception-throwing
image
Is it a good idea use exceptions? Maybe it's better having allways some kind of response from each operation? I guess this kind of questions are natively solved in F# as it forces always an output from all operations.

Do you have a better solution in this case?

I don't know how to bubble up errors without throwing exceptions without adding lots of complexity. If C# would have DU types it could be quite easy, but otherwise it will be tuples everywhere.

I don't know how to bubble up errors without throwing exceptions without adding lots of complexity.

I agree with this. I don't think this is wrong in this case.

Frikki commented

An exception should be thrown when a function experiences a failure, i.e., an error.

A function is a unit of work, and failures should be viewed as errors or otherwise based on their impact on functions. Within a function f, a failure is an error if and only if it prevents f from meeting any of its callee’s preconditions, achieving any of f’s own postconditions, or reestablishing any invariant that f shares responsibility for maintaining.

There are three different kinds of errors:

a condition that prevents the function from meeting a precondition (e.g., a parameter restriction) of another function that must be called;
a condition that prevents the function from establishing one of its own postconditions (e.g., producing a valid return value is a postcondition); and
a condition that prevents the function from re-establishing an invariant that it is responsible for maintaining. This is a special kind of postcondition that applies particularly to member functions. An essential postcondition of every non-private member function is re-establishing its class’s invariants.
Any other condition is not an error and should not be reported as an error.

Report an error wherever a function detects an error that it cannot deal with itself, preventing it from continuing in any form of normal or intended operation.

Handle the error in places with sufficient knowledge to handle the error, translate it, or enforce boundaries defined in the error policy, such as on main or thread mainlines.

Source: C++ Coding Standards: 101 Rules, Guidelines, and Best Practices

Not sure how that helps.

By the way, I was discussing this issue with some people, in particular with Jérémie Chassaing. He lives in a functional world, and, to my surprise, the Decider pattern proposed by Jérémie, doesn't have a switch back to return an error result. When I asked him about it he said "I just throw an exception". Using similar arguments like "it's a normal flow, etc" I tried to convince him otherwise. His answer was "It is a bug in your app. Command handling should not fail. All the preconditions can be checked upfront, and failing in the domain model is the last resort".

In the case of my sample app, there are two ways to avoid the exception:

  • Add the availability check to the (missing) UI. Overall, I have never seen a case at booking.com (for example) when I try to book a room and get "room already unavailable" error. They just don't show me rooms which aren't available. The domain model can still check availability and crash, there's nothing wrong with that as the request was invalid (obviously).
  • Move the call to IsRoomAvailable to the command service. It can return an error result. The domain model would operate with the assumption that the room is available.