Use Results Pattern Over Exceptions


Jun 5, 2024

ACCEPTED

Daniel Mackay, William Liebenberg

#result-pattern #exceptions

Technical Story: https://github.com/SSWConsulting/SSW.CleanArchitecture/issues/242

Context and Problem Statement

Using exceptions allows us to have global handling for certain types of errors (server error, bad request, not found, etc). They keep our code minimal as we don't need to add specific handling into every API endpoint. However, we still need to ensure these status codes are documented correctly.

The problem with this approach is that we're using exceptions as flow control. In other words, if a behavior is expected in your application (like a validation error), this is not an exceptional circumstance.

According to David Fowler exceptions are very expensive in ASP.NET Core.

There are several problems with using exceptions as flow control:

  • Performance - Exceptions are also very expensive to throw and catch
  • Breaks Principle of Least Astonishment - It's not obvious what exceptions can be thrown from a method. You need to look deep into the code.
  • Flow Control - Exceptions are complicated goto statements
  • Exception Groups - Exceptions can be grouped into 'expected' and 'unexpected' exceptions. It can be difficult to know which is which.

Considered Options

  1. Use Exceptions for Validation Flow Control
  2. Use Result Pattern for Validation and Flow Control

Decision Outcome

Chosen option: "Option 2 - Use Result Pattern for Validation and Flow Control", because exceptions should only be used for exceptional circumstances, and NOT for flow control.

Consequences

  • Dependency on additional nuget packages

Pros and Cons of the Options

Option 1 - Use Exceptions for Validation Flow Control

As per the current implementation.

  • ✅ Simple
  • ✅ Less code
  • ❌ Performance cost
  • ❌ Using exceptions as flow control (expensive 'goto' statements)
  • ❌ Breaks Principle of Least Astonishment
  • ❌ Exception Groups - not obvious which exceptions are expected and which are unexpected

Option 2 - Use Result Pattern for Validation and Flow Control

  • ✅ Improved Performance - no exceptions thrown
  • ✅ Explicit flow control
  • ✅ Adheres to the Principle of Least Astonishment
  • ✅ Exception Groups - it's clear that exceptions are real problems
  • ❌ More verbose code in the minimal API
  • ❌ It's possible to forget to check specific results

The result pattern could be implemented via: