Exception Aggregation — a new validation pattern

Romany Saad
May 2 · 6 min read

Validation is the way we guarantee a system’s integrity and consistency, therefore, validation is found in almost every piece of software no matter how tiny or large.

This is the first in a series of articles about validation, click follow to get updates about coming articles!

Introduction

From a validation perspective, we can break most software systems into 3 major parts: Use-Cases, Entities and builders, and Datastores.

Figure 1: validation constraining relation to context awareness

Use-case is a usage scenario for your entities and other system components to satisfy a business need, therefore a use-case is the most context-aware component of your system because it holds the truth about the user intent for using the system in a certain way.

we can specify all the validation a use-case needs without relying on any other part in the system, but some of these rules are not for any use-case to violate, rather they span many use-cases and scenarios, these are called “Invariants”.

an invariant is a set of assertions that must always hold true during the life of an object for the program to be valid.

Therefore, it’s better to encapsulate these in their related objects which guarantees that any client code that uses these objects will accordingly be enforcing these rules, therefore invariants are crosscutting in your application, we call objects that hold the invariant business rules “Entities”.

some rules are of another nature like concurrency issues and state persistence can be — in some cases — delegated to the Datastore, but a data store has to be more tolerant than a use-case or an entity about the validations it can apply, Datastores merely can do any help in regard of validation, because it has to make room for the variance in entities state (like nullable fields for example).

Validation and Error reporting

While validation is crucial to almost all systems but it also can be harmful if not correctly applied, for instance, incomplete error feedback from the back-end can harm your user experience, also the non-uniformity error handling and reporting can result in a buggy system or at best one that is hard to debug.

In my experience, I encountered 2 ways for validation, either by throwing
“Exceptions” or returning “Validation Errors” and both have some serious disadvantages. in this article, I want to introduce you to a third pattern that combines the best in both.

As you can see in the above example the thrown `Exception` will break the continuity of code execution, they will not allow for further error handling, which makes error reporting very cumbersome and doesn’t allow for collecting more errors.

We should not return error by error on each request as this can ruin the UX.

Imagine a user seeing an error for a “short name”, then after fixing and re-submitting we send him another error about an “illegal character” on the same field. that would be frustrating.

Pros:

  • prevents the construction of Invalid Entities.

Cons:

  • Breaks code execution continuity.
  • Returning error by error can break the user experience.

we can use some sort of error collection mechanism in our methods and use the bag of errors as the return value for our methods that do the validation which solves the problem of breaking the continuity of code execution.

At the first glance returning a collection of errors, might seems like a good idea, But as you can see, on the other hand, this will pollute your design making most of the return types of type ErrorBag as too many methods will have some validation logic in it.

this alone will prevent you from using meaningful return types, preventing your interfaces from revealing the real intent behind them which will, in turn, make the design in worse condition.

but let’s have another look at the client code as well:

on const errBag1 = x.setAge(16); yes, we are returning an error bag but still, we are relying on the client developer being vigilant enough to use these return types as he/she can easily dismiss the returned value resulting in an invalid entity that can be stored and operated on, which in turn violates the principle of always valid entities.

Read more about always-valid entities by Greg Young: here.

Pros:

  • A nicer way of returning errors to the user.

Cons:

  • possible dismissal of the returned errors will result in invalid entities being processed.
  • strongly harms the interface design.

The proposed solution here is What I call “Exception Aggregation” — IDK if this is a good name, which is a pattern that combines both solutions, reporting using exceptions (without breaking)* the code execution continuity.

First, the AggregateException , a class that itself inherits from the Exception base class (in typescript it's Error class but I use Exception here not to be confused with custom errors solution for non-typescript users).

And the client code will look something like this:

or even better:

While this obviously requires more keystrokes than normal flow, but this simple pattern forces the client to proactively handle Exceptions by aggregating them, without breaking the execution continuity also without sacrificing the entities’ interface cleanliness or communicating its intent.

Pros:

  • Forces the client to proactively handle Exceptions.
  • It does not sacrifice your design.
  • A nicer way of returning errors to the user.

Cons:

  • requires the usage of closures.

NOTE 1: the best place for this pattern to be used is where you construct or reconstruct your entities like Factories, Builders, …etc, where errors will be proactively handled, where you are able to control whether to return a valid entity or throw the Aggregated Exceptions.

Conclusion

“Exception Aggregation” is a good way to handle errors without breaking the continuity of code execution as it combines the best of both worlds without sacrificing your design or breaking user experience.

This pattern can be combined with mappers to loop through the errors in the Aggregate Exception and return more descriptive validation errors for the client-side apps.

There is much more to say about validation, but I wanted to start with introducing this pattern as it has proven useful.

If you find this article useful, please share it and follow for more articles like this.

Nerd For Tech

From Confusion to Clarification

Nerd For Tech

NFT is an Educational Media House. Our mission is to bring the invaluable knowledge and experiences of experts from all over the world to the novice. To know more about us, visit https://www.nerdfortech.org/. Don’t forget to check out Ask-NFT, a mentorship ecosystem we’ve started

Romany Saad

Written by

Software Engineer

Nerd For Tech

NFT is an Educational Media House. Our mission is to bring the invaluable knowledge and experiences of experts from all over the world to the novice. To know more about us, visit https://www.nerdfortech.org/. Don’t forget to check out Ask-NFT, a mentorship ecosystem we’ve started

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store