Exceptions are terms of the Ubiquitous Language
04 Mar 2013 » Giacomo TesioIn the previous article we have already saw that exceptions are normal results of reliable computations. But [exceptions] are well known element of our daily life.
The etimology of exception comes from the latin excipiō, composition of ex and capiō. Literally something that “take out” of something (a process, a rule, a law and so on).
Since human experience leaves sediment in the languages that traverses, modern language designers simply borrowed this concept to denote conditions that move the computation out of the desired path.
In domain driven design, we distill a code model from the language that the domain expert talks when he solves the problem. During the modeling session (or the lesson, from the expert’s perspective), the modeler asks many questions about the behaviours of the system and, as a senior coder, he try to explore borderline cases.
From such analisys, we often get a deeper insight of the model that can either confirm or not the previously defined terms. Sometimes it leads to deep refactoring (like when you see that a new concepts interact heavily with the previously modeled ones), sometimes it can even lead to (almost) restart from scratch. But often, borderline cases are either senseless or prohibited (in the context under analisys).
An example from real world
Let consider simple case: a command that registers in an investment proposal an order to dismiss a financial instrument that the customer does not own.
Short selling is a well known practice in financial market, but it’s senseless when you talk with our own financial advisor.
Indeed, when I asked to the domain expert how to handle this case he said: “it’s childishly simple, going short is not allowed here!”.
What’s this, if not an exception thrown at me?
Thus I simply made the expert objection explicit in the model with a class
named GoingShortIsNotAllowedException
. Simple enough.
Such an exception is a normal outcome of the advisory process, so we have to explicitly model it as a normal and well documented computational result. The application that use the domain can prevent that such a situation occurs or not, but the business’s invariant is nevertheless enforced by the domain. Still any coder that uses the domain has to ponder whether to catch such exception (and may be present it to a user) or ignore it (turning it in an error that should crash the application itself). Thus our work is still incomplete. We have to provide more informations to the client. What financial instrument has caused such exception? From which dossier?
Expressive exceptions
Expressive exceptions expose useful properties to the clients.
They help the user a lot, since through a proper UI rappresentation of the
exception, he can understand why his own request cannot be satisfied.
In applications used all over the world, expressive exceptions simplify
localization and internalization, since useful properties can be shown
differently at different latitudes, according to the user culture
(just like any other value object).
Moreover they can halve maintenance costs, since developers can
rapidly identify what’s happened and why from logs.
A cheap but very useful practise is to throw useful messages with
exceptions. This is particularly important when an exception can be thrown in
more than situation. For example, you can get a lot more from a
KeyNotFoundException
with a message containing the misspelled key.
Exception chaining is another important technique that allows clients to further understand why an exception occurred.
These look as common sense suggestions, but more often than not, good
developers under pressure think that they can be faster, agile and leaner
ignoring exceptional paths. Unfortunatly, this is true only for disposable
prototypes.
On applications that will run in production, well designed exceptions
pay well on long term (unless maintenance fees are your business model).
A final tip
We all know that a IL knowledge in a resume makes you look like a nerd.
Still, sometimes, it can make your life a lot easier.
If you occasionally avoid exception chaining and you want to re-throw a caught
exception, you should remember to use the throw
keyword without specifying
the caught exception.
Indeed, there are two distinct IL instructions that throw exceptions (actually
not only exceptions, but this is off-topic here), namely throw
and rethrow
.
While throw
pops the exception from the stack, reset its stack trace and
throws it to the caller, rethrow
just rethrow the exception that
was caught (it’s only allowed within the body of a catch handler).
Thus in the following CS code you will lose the stacktrace forever, since
it’s compiled to a throw
instruction:
If you really can’t wrap an exception that you caught before throwing it
again, remember to rethrow
it as in the following snippet:
This simple trick will help you (and your colleagues) a lot.
[1 ^] This is often what
makes the difference between a junior and a senior modeler:
while the junior one is fully focused to model rightly the intended business
behaviour, the senior one always keeps an eye open upon unusual cases.
Indeed even the domain expert often ignores how many borderline situations he daily
handles by borrowing from his own experience in the business. But since the
application will be based on such experience, we have to encapsulate it in the
domain model, and thus we need to turn that knowledge conscious and explicit.
This is a two fold aspect of DDD: it's more than an expensive software
development process, it's a tool to improve the customer understanding of its
own business. And believe me, a lot of the customer's business success comes
from his (almost inconscious) ability to identify such cases and properly
handle them.