Exception handling very easily gets ugly. Typical try...catch block clutters method and grows with any new exception discovered. Then, bits of code are copied between methods which require same error handling. Adding any new logic into error handling is a nightmare and with each new release it seems like the same errors are coming back.

Policies for handling exceptions

To overcome those problems we can extract logic related to exception handling into separate objects – policies. This will keep main business logic clear, allow reusing and make testing easy.

Here’s the definition for recoverable policy:

public interface IRecoverablePolicy<TResult>
{
   TResult Execute(Func<TResult> operation);
}

One example of recoverable policy is handling transient exceptions. Usually they require retrying method call after a small pause.

public class TransientExceptionRecoveryPolicy<TResult> : IRecoverablePolicy<TResult>
{
    public TResult Execute(Func<TResult> operation)
    {
        try
        {
            return operation.Invoke();
        }
        catch (TransientException)
        {
            Thread.Sleep(TimeSpan.FromSeconds(1));
            return Execute(operation);
        }
    }
}

Now, extending this code with a functionality to retry N times with and exponentially increasing backoff between attempts is fairly easy. Also, unit testing is straight forward. The policy can be reused to protect any method call which may throw a TransientException.

The policy for handling communication exceptions is very similar to the above.

To execute code with the policy we call it like this:

policy.Execute(()=> { // call a method on external service });

Handling functional cases

Above examples were covering non functional cases where external service may give transient error. But we can cover functional cases as well. Let’s imagine a scenario where we call a method to create an article and get back article ID. Once article is created we want to further use it’s ID, let’s say to store it in the system. The article title has to be unique, and when uniqueness is not satisfied, the DuplicateTitleException is thrown. Assuming we are not using any transactions, there potentially is a risk of operation failing between creating and article and storing it’s ID. To make the operation idempotent we need to detect DuplicateTitleException, fetch article’s ID and carry on. This can be easily achieved by wrapping call to create an article in the policy which on failure will get the ID and return it back. Below is an example implementation:

public class DuplicateTitleRecoveryPolicy : IRecoverablePolicy<long>
{
    private readonly IExternalService _service;
 
    public DuplicateTitleRecoveryPolicy(IExternalService service)
    {
        _service = service;
    }
 
    public long Execute(Func<long> operation)
    {
        try
        {
            return operation.Invoke();
        }
        catch (DuplicateTitleException e)
        {
            var article = _service.GetArticleByTitle(e.Title);
            return article.Id;
        }
    }
}

Composite policy

To cover the code with few policies, we can use a CompositePolicy which will execute set of policies one after another:

public class CompositeRecoverablePolicy<TResult> : IRecoverablePolicy<TResult>
{
    private readonly List<IRecoverablePolicy<TResult>> _policies;
 
    public CompositeRecoverablePolicy(IEnumerable<IRecoverablePolicy<TResult>> policies)
    {
        _policies = new List<IRecoverablePolicy<TResult>>(policies);
    }
 
    public TResult Execute(Func<TResult> operation)
    {
        var chainedPolicies = operation;
 
        foreach (var policy in _policies)
        {
            var localOperation = chainedPolicies;
            var currentPolicy = policy;
            chainedPolicies = () => currentPolicy.Execute(localOperation);
        }
 
        return chainedPolicies.Invoke();
    }
}

We pass all the policies required to cover the code in the constructor, and use composite policy to execute the code.

Extension methods

To further increase readability of the code we can use extension methods and create overloads which take policy as an extra parameter:

public interface IExternalService
{
    long CreateArticle(string title, string author, string body);
}
 
public static class ExternalServicePolicyExtensions
{
    public static long CreateArticle(this IExternalService service, string title, string author, string body, IRecoverablePolicy<long> policy)
    {
        return policy.Execute(() => service.CreateArticle(title, author, body));
    }
}

Now, the call to create an article will look more familiar:

var articleId = service.CreateArticle("post", "me", "empty", policy);

Sample code

The sample code can be found on github.

Summary

Policies are very powerful tool. They allow to decouple exception handling from the main code. This leads to smaller and simpler classes which adhere to Single Responsibility principle. By using a strategy pattern we also follow Open/Closed principle and make the application easy to extend. Policies are easy to reuse, hence it is easier to prevent code duplication (DRY principle). Each policy is treating single case and is completely independed from other business code which makes them extremely easy to unit test.

The best place to use policies is when making network calls, using external service or need to respond and recover from business errors.