Distinguish Business Exceptions from Technical

From WikiContent

(Difference between revisions)
Jump to: navigation, search
m (Changed to conform with standard layout)
(Edit after feedback - ended up longer ...)
Line 1: Line 1:
-
There are basically two reasons that things go wrong during runtime. Either there is some technical problem or there is some business logical reason that we cannot proceed. Both of these are situations are usually signalled through exceptions. However, the situations are so different that they should be carefully held apart. As a contrast, it is for example a dangerous source for confusion by representing them both using the same exception hierarchy, not to mention using the same exception class.
+
There are basically two reasons that things go wrong during runtime. Either there is some technical problem or there is some business logical reason that we cannot proceed. Most modern languages such as LISP, Java, Smalltalk, and C# use exceptions to signal both these situations. However, the two situations are so different that they should be carefully held apart. As a contrast, it is for example a dangerous source for confusion by representing them both using the same exception hierarchy, not to mention using the same exception class.
-
When an irresolvable technical problem occurs, there is usually not much the calling client code can do about it. If for example a necessary database does not respond, you must assume that the infrastructure has done what it can to try to resolve the situation, e g repairing connections and retrying a fair amount of times. So, as a client there is not much idea of catching and handling these kinds of exceptions. Instead we probably let them bubble up to the highest architectural level and let some general exception handling mechanism do what it can to ensure the system is in a safe state (ensuring transaction rollback et al), log and alert administration, and report to the user in some neat way.
+
When an irresolvable technical problem occurs, there is usually not much the calling client code can do about it. For example, if you make a call to some library code resulting in an attempt to access array element 83 from an array of size 17, then the program is clearly off track and the library code will throw some kind of array-index-out-of-bound exception. As a client there is not much idea of catching these kinds of exceptions – if you knew the array was too small, you wouldn’t have made call in the first place.
-
Obviously exceptions signalling irresolvable technical problems should be unchecked so that they transparently can “bubble through”. Some infrastructure APIs, e g “java.sql”, force their clients to catch and handle exceptions, and in that situation there is basically one reasonable thing to do – wrap the exception in some unchecked exception, add contextual data, and re-throw the wrapped version. If you are in an EJB container, then the EJBException (which extends RuntimeException) was created for precisely this reason, and other frameworks have a similar construct. In general decent choices could be IllegalStateException or RuntimeException itself.
+
Instead of catching and handling irresolvable situations, we probably let the exception bubble up to the highest architectural level and let some general exception handling mechanism do what it can to ensure the system is in a safe state (ensuring transaction rollback et al), log and alert administration, and report to the user in some neat way.
-
A variant of an irresolvable technical problem is if you realise that the client calling your code has made a mistake. E g you could realise that they have called your method with a totally bizarre argument, or that they have not made the proper setup of a dependent object. Actually, this is as bad as trying to get the 47th element out of an array of length 32. The caller should have checked, and not doing so is a breach against the contract of your method and a programmer error on the client side. The proper response again is throwing some unchecked exceptions, e g IllegalArgumentException or IllegalStateException.
+
A variant of this situation is when you are sitting in the “library situation” and realise that the client calling your code has made a mistake. E g your method might have been called with a totally bizarre argument, or the caller might have not made the proper setup of a dependent object. Actually, this is a code defect as bad as trying to get the 83th element out of 17. The caller should have checked, and not doing so is a breach against the contract of your method and a programmer error on the client side. The proper response is throwing some exceptions, e g illegal-argument-exception or illegal-state-exception.
-
In sharp contrast to these, we have the situation where you cannot complete the call for a domain logical reason. In this case we have encountered a situation that is an exception, i e unusual, but not bizarre or erroneous in any way. For example, if I try to withdraw money from an ATM but balance is not sufficient, it is an unusual situation but not un-normal. In other words this kind of situation is a part of the contract, and throwing an exception is just an “alternative return” that is part of the model and that the client should understand well, take into account and be prepared to handle. For these situations it is suitable, and advisable to create a specific exception, or a separate exception hierarchy so that the client can precisely catch and handle the situation, e g InsufficientBalanceException. If the exceptional situation is critical to handle, you might even consider making this a checked exception thus taking the compiler to assist in forcing your clients to consider this special case. That design choice is however far from clear-cut.
+
A different, but still technical, situation is when the program cannot proceed due to some deficiency in the runtime environment, for example if a necessary database does not respond. In this situation you must assume that the infrastructure has done what it can to try to resolve the situation, e g repairing connections and retrying a fair amount of times. Even if the cause is different, the situation for the code is similar: there is nothing to do about it. So, we signal the situation through an exception that we let bubble up to the general exception handling mechanism. This is also the behaviour seen in several frameworks such as Spring or Hibernate.
 +
 
 +
In sharp contrast to these, we have the situation where you cannot complete the call for a domain logical reason. In this case we have encountered a situation that is an exception, i e unusual, but not bizarre or erroneous in any way. For example, if I try to withdraw money from an ATM but balance is not sufficient, it is an unusual situation but not un-normal. In other words this kind of situation is a part of the contract, and throwing an exception is just an “alternative return” that is part of the model and that the client should understand well, take into account, and be prepared to handle. For these situations it is suitable and advisable to create a specific exception or a separate exception hierarchy so that the client can precisely catch and handle the situation, e g insufficient-balance-exception.
Mixing technical exceptions and business exceptions in the same hierarchy blurs the distinction and confuses the caller about what the method contract is, what conditions it is required to ensure before calling and what situations it is supposed to handle. Separating the cases gives clarity and increases the chances that technical exceptions are let to be handled by some application framework, while the business domain model exceptional situations actually are considered and handled by the client code.
Mixing technical exceptions and business exceptions in the same hierarchy blurs the distinction and confuses the caller about what the method contract is, what conditions it is required to ensure before calling and what situations it is supposed to handle. Separating the cases gives clarity and increases the chances that technical exceptions are let to be handled by some application framework, while the business domain model exceptional situations actually are considered and handled by the client code.

Revision as of 08:00, 28 November 2008

There are basically two reasons that things go wrong during runtime. Either there is some technical problem or there is some business logical reason that we cannot proceed. Most modern languages such as LISP, Java, Smalltalk, and C# use exceptions to signal both these situations. However, the two situations are so different that they should be carefully held apart. As a contrast, it is for example a dangerous source for confusion by representing them both using the same exception hierarchy, not to mention using the same exception class.

When an irresolvable technical problem occurs, there is usually not much the calling client code can do about it. For example, if you make a call to some library code resulting in an attempt to access array element 83 from an array of size 17, then the program is clearly off track and the library code will throw some kind of array-index-out-of-bound exception. As a client there is not much idea of catching these kinds of exceptions – if you knew the array was too small, you wouldn’t have made call in the first place.

Instead of catching and handling irresolvable situations, we probably let the exception bubble up to the highest architectural level and let some general exception handling mechanism do what it can to ensure the system is in a safe state (ensuring transaction rollback et al), log and alert administration, and report to the user in some neat way.

A variant of this situation is when you are sitting in the “library situation” and realise that the client calling your code has made a mistake. E g your method might have been called with a totally bizarre argument, or the caller might have not made the proper setup of a dependent object. Actually, this is a code defect as bad as trying to get the 83th element out of 17. The caller should have checked, and not doing so is a breach against the contract of your method and a programmer error on the client side. The proper response is throwing some exceptions, e g illegal-argument-exception or illegal-state-exception.

A different, but still technical, situation is when the program cannot proceed due to some deficiency in the runtime environment, for example if a necessary database does not respond. In this situation you must assume that the infrastructure has done what it can to try to resolve the situation, e g repairing connections and retrying a fair amount of times. Even if the cause is different, the situation for the code is similar: there is nothing to do about it. So, we signal the situation through an exception that we let bubble up to the general exception handling mechanism. This is also the behaviour seen in several frameworks such as Spring or Hibernate.

In sharp contrast to these, we have the situation where you cannot complete the call for a domain logical reason. In this case we have encountered a situation that is an exception, i e unusual, but not bizarre or erroneous in any way. For example, if I try to withdraw money from an ATM but balance is not sufficient, it is an unusual situation but not un-normal. In other words this kind of situation is a part of the contract, and throwing an exception is just an “alternative return” that is part of the model and that the client should understand well, take into account, and be prepared to handle. For these situations it is suitable and advisable to create a specific exception or a separate exception hierarchy so that the client can precisely catch and handle the situation, e g insufficient-balance-exception.

Mixing technical exceptions and business exceptions in the same hierarchy blurs the distinction and confuses the caller about what the method contract is, what conditions it is required to ensure before calling and what situations it is supposed to handle. Separating the cases gives clarity and increases the chances that technical exceptions are let to be handled by some application framework, while the business domain model exceptional situations actually are considered and handled by the client code.


By Dan Bergh Johnsson

This work is licensed under a Creative Commons Attribution 3


Back to 97 Things Every Programmer Should Know home page

Personal tools