In our system design we have following layers:
Web API -> BusinessLayer -> HelperLayer -> DataLayer - Call hierarchy
Web API is the Rest service layer, Business does business operation on the business entity, Helper does the transformation of Data entity to Business Entity and Data fetch the POCO from database
As we are discussing the exception management strategy of the system, following are the two views:
I prefer that all errors shall propagate till the Web API, where we use Error filter to intercept, log the errors and change Context.Response to provide a friendly message to the end user, benefits of the same is:
Error source remains intact
We handle the exception where its required
Simple and straightforward mechanism for error handling
What another set of team mates prefer is we create a custom exception for each layer, like DALException, HelperException, BusinessException, where a given layer throws the Exception, calling layer handles it, fills the inner exception and thus continue, as per them the benefit is:
- Each layer can provide a custom information of the issue / exception, which would help in error / exception abstraction
For me the the issue with this design is:
- Changing the source of exception which is nopt a good practice
- Catching exception without any processing
- Lot of extra code by adding try catch everywhere, which can impact performance in my understanding
Only benefit I see is we can provide specific message to the customer, but that is even possible, if we understand the underlying core exception and differentiate based on some code and thus provide a custom message like ABC failed instead of a generic message.
Please share your view and let me know if a clarification is required
I suggest avoiding the question (somewhat) by using interceptors rather than placing any of that logic directly in your classes and methods.
If the responsibility of a class is to receive a request for some data and return it from SQL then it shouldn’t be concerned with which types of exceptions which layers expect. That exception handling becomes additional logic which is outside of that class’s responsibility.
There are a number of different ways to implement interception. It might depend on what tools are already incorporated in your applications. I use Windsor for dependency injection so it’s convenient to use their interceptors. If I wasn’t using Windsor then I’d look at PostSharp.
But the net effect is that you either have an attribute on your class or a declaration when you declare your dependencies, and then all the logic of “this type of exception was thrown, catch it, wrap it and that, and re-throw” all lives in an interceptor class. You can change it back and forth without polluting your other classes.
99% of the time this enables me to have no
try/catch blocks in my code at all. Logging and rethrowing is relegated to the interceptors. The only time I have any exception handling is if I need to handle something gracefully, so I need to catch an exception, log it, and return a non-exception result.
Unrelated to interception:
In practice, I’ve found that most of the time having one type of exception vs. another or wrapping exceptions on other types is useless. 99% of the battle is just having the exception detail vs having nothing. As long as no one does
throw ex (obliterating the stack trace) then you’ll have what you need in the exception detail. If we get clever with wrapping exceptions in more exceptions then we just create more information that we’re going to ignore. We’ll have more detail to sift through as we look for the one thing we actually care about – what was the exception and where was it thrown?
The only exception (I really wish I had a synonym for that) is if the business layer throws an exception containing user-facing information. For example, the user tries to update something, they can’t, and you want to wrap the exception so that it explains what they need to correct. The specific type of exception could indicate that the exception is user-facing.
But if the exception message is the result of business logic (“You can’t place this order because the item is out of stock”) then is that really an exception at all? Perhaps that call should return a failure message rather than throwing an exception. We shouldn’t use exceptions to communicate messages.