Using smartics-exceptions

What are best practices for using the smartics-exceptions library?

Dependency Magnet

If all error codes of an application are stored in one enumeration, this enumeration becomes a, as Robert C. Martin calls it, dependency magnet. Any change to the enumeration will require a redeployment of all modules that use it. Developers will get reluctant to make changes to it. Therefore it is a best practice to provide an enumeration per package dealing with a certain kind of problem. Changes to the enumeration will then only have local effect.

Error Code Index

For small projects it is helpful to create a class of constants listing your exception code ranges. This makes it easy to check which ranges are already used. Since you are designing your error codes in advance, this class should not be subject to many changes.

The error code index class may look like this:

package de.smartics.example.util;

/**
 * Constants to define number code ranges.
 */
public final class ApplicationNumberCode
{
  /**
   * The start number of the system codes.
   */
  public static final int SYSTEM_CODE_START = 0;

  /**
   * The start number of the security codes.
   */
   public static final int SECURITY_CODE_START = 1000;

  /**
   * The start number of the validation codes.
   */
  public static final int VALIDATION_CODE_START = 2000;

  // ... add further code ranges here...

  /**
   * Constant class.
   */
  private ApplicationNumberCode()
  {
  }
}

This will work well for small applications. On a larger scale you may provide some of those classes for each module to keep the coupling low. This will also help to fend off the Dependency Magnet problem.

Message Bundles for Exception Codes

The message bundles should have the base name of the codes implementation (usually an enumeration) with the suffix Bundle and be placed in the same package.

de.smartics.example.service.security.SecurityServiceNumberCodeBundle.properties

de.smartics.example.service.security.SecurityServiceNumberCode.java

Never analyze the Code Parameter in the Catch Block

Should we provide one code number per exception or is it appropriate to have a couple of codes for one exception class? Basically it is convenient and effective to have one exception class that is configured with different codes, provided that

  1. the logical exceptions (discriminated by the codes) sharing the same class do not require different exception information (modeled as properties of the exception class).
  2. handling the exception after catching it, does not require to look up the code.

If any of the above facts are offended, write a new exception class. If an exception class only supports one code and will never happen to support more than one code, then hard code it into the exception.

And stated positively: Write different exception classes only if the exception is to be caught in a catch block and handled individually. Following this rule keeps the number of exception classes small.

This tip is related to Do not analyze caught Exception.

One Code Enumeration per Component

We recommend to write on code enumeration per component of your software system. If your software system is small, one code enumeration may be sufficient. If it is composed of large components, you may want to subdivide components, giving them names like componentA/subcomponentX.

You may decide to continue counting independent of the component:

  • componentA-1000
  • componentB-2000
  • componentC-3000

or you may start the number code with each component new from one:

  • componentA-1000
  • componentB-1000
  • componentC-1000

It mainly depends upon whether you want to have a unique number that stands for its own or if you have numbers with a alphanumeric prefix (i.e. the component ID).

Use specific Code Types

If you pass a code to a constructor use the most specific code. Therefore use

public MyException(final MyCode code, ...) { ...

instead of

public MyException(final Code code, ...) { ...

This makes the code more readable and users know which codes to pass in.

If you need codes from different enumeration to be provided as arguments you may define a tagging interface that is implemented by all enumeration types.

public interface AnyServiceCode extends NumberCode {
}
public enum ServiceCode implements AnyServiceCode { ... public enum RespositoryCode implements AnyServiceCode { ...

Exception Code to Exception Class Relationship

There is no strict requirement for a 1:1 relationship of exception codes to exception classes.

If there is a type of exception that provides the same information in different contexts, it is an effective solution to pass different codes to create exception instances. So you have N exception codes for one exception class (N:1).

Examples are some kind of database query exceptions, where the exception instance provides information about the connection to the database and the query. Dependent on which type of query has been executed, the exception code may differ, while the exception class in use is the same. Note that the message templates may also differ, since there is a 1:1 relationship between codes and message templates.

On the other side it is also a valid approach to pass the same code to instances of different exception classes (1:N). You may have a base exception that provides all information and derive subclasses that share the same code, but have to allow to be caught and handled individually. But if these exceptions have to alter the message text, different codes have to be used.

The above example of database exceptions can also be modeled this way. For instance there is a base DatabaseQueryException and subclasses like InvalidQuerySyntaxException, NoResultException, TooManyResultsException. Especially if the exceptions are handled internally and only occasionally be logged, this approach may safe development time. But note that all these exceptions provide the same message and exception code to the log.

In situations where different information has to be passed to different message templates, there is the need for one code per exception (1:1). This is also the case if you want different codes and also be able to catch different exception causes individually.

This approach provides detailed information to the operations team (like N:1) and allows the development team to deal with each problem individually (like 1:N). It also supports to provide problem specific information for each exception (supported by neither N:1 nor 1:N). The drawback is that you have to implement one exception class and one message template per code. Fortunately this is not much work, if you use smartics-exceptions, but it is work nonetheless. It is the recommended approach, if you require verbose error reporting.