This is an almost random collection of thoughts on exception handling. Hopefully reading it would make it clear what kind of problems VITA is trying to address in this area. The other goal is to discuss some controversies and misconceptions surrounding the exceptions.
Exception handling - a common anti-pattern
I have seen this happen more than once, and if you are a developer with a few projects in the resume, chances are you had seen this too.
The story unfolds like this. A new project is started. Requirements and specs are ready (or not), the development is in full speed. Parts of the future application start to evolve, and something actually works, some shiny UI is up and you can do something with the app. Then comes up the issue of error handling - errors happen, need to be handled and logged. If it's not yet done - no problem, a global catch block is added, other catch blocks are sprinkled throughout the code. The catch blocks log the exception using some logging facility - home-baked, or Log4Net, or maybe you decide to use this huge pile of nonsense called "Exception Handling Block" in Enterprise Library - whatever, everything seems under control.
And then problems start to surface. It appears that some exceptions are different from others. Even if they are of the same type. For example, _InvalidArgumentException_ may result from a serious bug, but it also surfaces when user enters an invalid value (or no value at all) in some field in UI form. In the latter case, it is not necessary to log a mile-long exception report and spam the developers - just show an error message to the user.
The first almost instinctive response is usually - OK, let's just make a catch block a little smarter, separate exception apples from oranges, and we'll be doing fine. For some time, it works. The code is "guessing" the exception kind by looking into the message and other "clues". But then the catch blocks becomes larger and more "sophisticated" and turn into a complete mess.
Suddenly everything is ruined by "globalization" requirement - all messages should be translated into different languages. Worse, even standard .NET exceptions will be localized - the app will be running on non-English computers, in which the .NET Framework speaks French or German.
It's not over yet, it appears that not all validation checks can be localized into a dedicated methods - as we thought initially, user error can surface from anywhere. Even batch process. Plus, you discover that a submitted UI form may have MULTIPLE user errors - let's try to pack all messages into a single exception.Message property using some delimiter like line-break... Except some messages contain line breaks. Hm, should we use Xml? Oops... UI says each message should be shown next to the control with the invalid value, so we need to pack property names with messages.
Damn, what to do about submitting duplicates of some records?! These are not detected in c# code, they are rejected by the database server, which throws SqlException - we need to start parsing this message as well...
And now the final blow - REST. Web service. The UI layer should not talk to application logic or database directly, it should communicate with remote RESTful API. It was initial requirement, we just delayed separating layers until later, when we have some basic code working. Now we have to come up with ways to transport our "exceptions" and deal with them on the client. And separate them from the added Web exceptions. And by the way, at the REST layer, we should follow REST rules for handling errors.
Yeah, that's gonna be a loooong night...
Sounds familiar? Details may vary, but the anti-pattern is clear. Exception handling (and user errors handling) should be addressed from the very beginning and built into the core system. Otherwise, you get into trouble.
VITA introduces a conceptual framework for proper exception-handling. It works for user errors (including multiple user errors), database-produced errors, and transparently works through REST while obeying the REST rules for error handling.
The central concept in this framework is _NonFatalException_ - a base class for all exception that do not indicate a bug or system malfunctioning - most likely a user error. The subclass of NonFatalException - ValidationException - holds a collection of errors, each having a message and additional information for pinning the message to a particular object or UI control. The exception is able to travel along the REST connection (as BadRequest response) and resurrects on the client, with all information in tact.
What always surprised me is that .NET base libraries (BCL) never introduced a special exception class for non-fatal errors - at least I was not aware of any such class. It seems so natural and so common to all applications - but it never surfaced in the basic framework. They did have an ApplicationException - a base class for all non-BCL exceptions, which proved to be useless, and was deprecated. And none of the existing frameworks (logging, exception handling, ORM) appear to address the problem properly.
Recently, my friend and active Irony contributor AlexeyY prompted me that there is in fact a WarningException class in the BCL that has somewhat similar purpose to VITA's NonFatalException. I was quite surprised that something like this escaped me, but Google search revealed why. Other than the MSDN documentation, it is virtually non-existent. No articles, no third party samples. Lot of Java links although - to namesake java class. And if the thing is not on Google, it does not exist. Apparently, Microsoft did not promote its use at all.
From the description in MSDN, it looks like the purpose of the exception is to show an error message to the user (instead of Exception Manager window). In that way, it is somewhat similar to VITA's ValidationException. Similar, but not quite the same. Lacking more information, it seems like the purpose of the exception is to carry a single(!) message to the UI to show it to the user - quite limited indeed.
I contemplated for a while whether VITA should use this exception type in place of NonFatalException, and decided not to do it.
- One reason is its obscurity bordering on non-existence - it is dangerous to base a fundamental feature on a not-well-known class.
- Secondly, there is a chance that existing of future MS or third-party code is/would treat this exception in a special way, not expected by VITA's code, so it might break the application.
- Finally, the name - seems like self-contradictory, an oxymoron. Warning is a question to ask before proceeding. Exception on the other hand is about stopping and canceling the current work. If we through exception - we are canceling the operation. What we show after that - is not a warning anyway.
That's why I decided to stay with a custom NonFatalException class.
Controversy: using exceptions for user errors
There is a big controversy - at least for some developers - about "proper" use of exceptions, and whether it is OK to use exceptions for things like user errors. The often repeated mantra is "use exceptions only for unexpected cases, and user error is an expected thing". If you are among those who is troubled with this dilemma - to throw or not to throw - here is my digest on the issue. I hope this will clear the air, and will let you accept the approach in VITA framework - it's OK to throw on user error.
- First of all - what the heck does this mean - "unexpected"?! Let's just look at a good old SqlConnection object in .NET data libraries. If you open a connection to the database, you can reasonably expect that sometimes the database would not be available. Right? it is a kind of "expected" case, at least you, the developer, can foresee it happening. Why then connection.Open() throws an exception when it fails to connect? Until I get the clear answer, or Microsoft changes the implementation of the SqlConnection class - I assume the objection does not hold any truth at all.
- The other often-sited objection is - "Exceptions are expensive, use it only when you have no choice. If you use it for user errors, your code will be really SLOOOOOW." Yes, if you use exceptions to return values from functions - you are doing non-sense. This crazy case aside, let's say you are in the server code and find an error in the data submitted by the user. At this moment all of the previous work of submitting request and handling it - it is already a waste! Throwing or not throwing does not change much the amount of wasted effort.
- Finally, if you are going to handle user errors using return codes instead of exceptions, at least in some parts of your code, then you'll have to switch ALL of your code to this error handling style. Do not think that user errors popup only in isolated methods that do validation - this is not the case in the real world. But using errors codes - oh, not that again. Welcome back to the 70's and all of the misery of languages without structured exception handling.
I hope this clears the issue. VITA uses a exceptions for signalling interruption events that are not caused by system failure, but by user errors. This allows you to write a well organized code with really structured exception handling.