Error Handling/Display in an n-tier Design?

  • Thread starter Thread starter Steve - DND
  • Start date Start date
S

Steve - DND

I was wondering if anyone can point me to some articles or practices they
use for dealing with errors in their applications; particularly in an n-tier
design. I am trying to find one way to conveniently handle both soft and
hard errors, primarily for display to the end user.

One method I have considered is rethrowing exceptions. With this approach, I
would throw a plain old ApplicationException, with the Message property as
the polite message for the user, and the InnerException as the exception
that actually occurred(although this might not be necessary, since the
actual error would be immediately logged for debugging purposes). The major
drawbacks I have seen with this, is that it appears to be a frowned upon due
to extra resource usage(does anyone have any hard figures?), and that
exceptions should be used for exceptional happenings(meaning that failing a
security check shouldn't mean an exception is thrown). One other major issue
is that it is difficult(if not impossible) to use in a scenario where some
steps in a process can fail and should be reported, but they do not mean
that the whole process failed.

The other method I have considered is maintaing an error collection that is
populated with errors when they occur, and passed around the business layer,
and data layer. This seems like the way to solve the issue of multiple steps
failing, and not adding the additional resource consumption by throwing
errors. The major issue it does bring up is that after every call to the BL,
we must check the error collection for errors, and output them if need be,
and stop further processing if necessary. Although this could be wrapped up
with a call to a static function somewhere after each call to the BL, it
still seems like it would be a maintenance nightmare in the future.

Any thoughts, ideas, current practices, or articles would be most
appreciated.

Thanks,
Steve
 
Hi Steve,

I don't have any article I could point you to. But overall, your strategies
seem right. My comments are in-line:

Steve - DND said:
I was wondering if anyone can point me to some articles or practices they
use for dealing with errors in their applications; particularly in an n-tier
design. I am trying to find one way to conveniently handle both soft and
hard errors, primarily for display to the end user.

One method I have considered is rethrowing exceptions. With this approach, I
would throw a plain old ApplicationException, with the Message property as
the polite message for the user, and the InnerException as the exception
that actually occurred(although this might not be necessary, since the
actual error would be immediately logged for debugging purposes).

This works really well. The user should only see the message of the top
level exception (maybe with a button to see the details), and you should log
everything (all messages + traceback) for debugging purposes.
The major
drawbacks I have seen with this, is that it appears to be a frowned upon due
to extra resource usage(does anyone have any hard figures?), and that
exceptions should be used for exceptional happenings(meaning that failing a
security check shouldn't mean an exception is thrown).

The extra resource usage is not a problem if you use it only for exceptional
happenings. And you have to, otherwise your exception logs will explode.

My recommendation is to use exceptions only for exceptional happening, i.e.
events that are due to unexpected I/O failures, bugs, installation problems
(file missing), etc. Everything else (for example, validating user input)
should be done through function calls, if/then/else tests, etc., not through
exception handling.

The case of security checks is interesting. My approach is that the BL layer
should throw exceptions when a security check fails but it should also
expose methods to test if an operation is allowed or not (security queries).
The UI layer should use these security queries to configure the UI and
inform the user when an operation is not allowed. If the UI layer is
correctly implemented, the BL layer should not throw any exception. In this
approach, exceptions are only used for exceptional happenings (security hole
in the UI layer).

Also this general approach (using exceptions only for exceptional
happenings) is in line with the "contracting" metaphor and the "disciplined
exception" methodology that B Meyer describes in Object-Oriented Software
Construction (Prentice Hall)
One other major issue
is that it is difficult(if not impossible) to use in a scenario where some
steps in a process can fail and should be reported, but they do not mean
that the whole process failed.

Yes, this is an issue. In this case, every process that is allowed to fail
should catch all exceptions, log them, and return some kind of error/warning
object to its caller. The higher level code should display these
errors/warnings to the user.
The other method I have considered is maintaing an error collection that is
populated with errors when they occur, and passed around the business layer,
and data layer. This seems like the way to solve the issue of multiple steps
failing, and not adding the additional resource consumption by throwing
errors. The major issue it does bring up is that after every call to the BL,
we must check the error collection for errors, and output them if need be,
and stop further processing if necessary. Although this could be wrapped up
with a call to a static function somewhere after each call to the BL, it
still seems like it would be a maintenance nightmare in the future.

Well, this is a very good way to handle the situation that you described
before (process that fails without causing the entire process to fail). We
actually use this in conjunction with exceptions. The idea is the following:

* At the low level, we use E.H. to signal abnormal situations.

* At the high level, we have processes that may recover. These processes log
the errors and populate collections of error objects that the top level uses
to inform the user.

This works quite well, but we don't pass an error collection everywhere. As
you say, this would quickly lead to a maintenance nightmare (and also a lot
more code in the first place).
 
Well, this is a very good way to handle the situation that you described
before (process that fails without causing the entire process to fail). We
actually use this in conjunction with exceptions. The idea is the following:

* At the low level, we use E.H. to signal abnormal situations.

* At the high level, we have processes that may recover. These processes log
the errors and populate collections of error objects that the top level uses
to inform the user.

This works quite well, but we don't pass an error collection everywhere. As
you say, this would quickly lead to a maintenance nightmare (and also a lot
more code in the first place).

Thanks for the input. I'm curious though. How exactly does the presentation
layer know that there is something to display in terms of messages? Does it
only check the errors collection at the end of the process(as opposed to
after every step as I had mentioned)?

Thanks,
Steve
 
Thanks for the input. I'm curious though. How exactly does the
presentation
layer know that there is something to display in terms of messages? Does it
only check the errors collection at the end of the process(as opposed to
after every step as I had mentioned)?

Yes, the UI checks if the collection is empty or not. If not, it displays
the list of errors (our app is a Web app, so the UI cannot intercept errors
in the middle, it can just submit, pray, and see the results).

The BL layer can also test the collection and skip some sub-processes if the
collection is not empty and the BL layer knows that these processes should
only be attempted when all the other sub-processes went ok before (but in
practice, there are very few cases where we can actually do this).

But the general idea is to limit the use of error collections to the few
places where we have steps that may fail without causing everything to fail
(there are usually few of these and they are usually at a rather high level
in the logic) and to use exceptions for all the rest (simpler, easier to
code, and usually more robust, even if it not always very subtle).

Of course, these are just indications on how we solved our problems. Yours
may be slightly different and may need a different cure.

Bruno.
 
Back
Top