Gotta catch ’em all

5 min readMar 25, 2021

I apologize if you’re expecting me to talk about pokemons but I’m actually going to address exceptions. The goal of this article is to present you a design pattern which allows you to intercept exceptions thrown in different parts of a project in a single code. First, let me tell you about the importance of having a good architecture with this small example. You hired an amazing new developer for one of your teams and he’s assigned to write a new feature. He does develop the new feature and it works well, but your user gets some unusual error message or maybe your client’s application crashes because your API is returning an unexpected response. Maybe there’s a problem with the new feature or there was just some connectivity issues.

A good architecture is no silver bullet, but it can help your company enforce policies and good practices.

Afterward, I expect you to know a little about object oriented programming (OOP) and exceptions. Just like pokemons they can easily escape if not addressed correctly. Unhandled exceptions can slip out of your code and end in the user’s hands, and what an unpleasant experience. Don’t underestimate them. Next, I’ll show you two ECMAScript implementations of a design pattern called proxy that will help you with that. The first one can be easily adapted to other programming languages. In OOP, the idea of a proxy is to intercept access to an object.

To give you some context, let’s say we’re working on packages with a single responsibility. In the first scenario, we’re working on a validator’s package and we expect all classes to have a common method named validate, so it’s a good idea to implement a Validator interface that enforces this method.

interface Validator {    validate() : any;}

We want to intercept all calls to the validate method of any object and will code a proxy class that implements the Validator interface. We will use the concept of composition to encapsulate a class that implements our interface. All access to an object of such class will be controlled by our proxy implementation and this is awesome because we can write a single piece of code to handle exceptions and errors coming from different implementations of the Validator interface. Here’s the implementation of our proxy.

class ValidatorProxy implements Validator {    validator : Validator;    constructor(validator : Validator) {        this.validator = validator;
}
validate() { console.log("ValidatorProxy is calling validate."); try { this.validator.validate(); } catch(error) { console.log("ValidatorProxy caught an error."); } }}

Everything works out well so far but there’s still something left we can improve by using another design pattern called factory. We will implement the factory pattern to create new objects encapsulated in the proxy class and will leave the ValidatorProxy transparent to other developers.

class ValidatorFactory {

create(type: (new () => Validator)): Validator {
const validator = new type(); return new ValidatorProxy(validator);

}
}

Amazing! Here’s an example of how one can use it and its output if executed.

class ClientValidator implements Validator {    validate() {        console.log("Validating client.");        throw Error("Couldn't validate client.");    }}const clientValidator = ValidatorFactory.create(ClientValidator);
clientValidator.validate();
[LOG]: "ValidatorProxy is calling validate."
[LOG]: "Validating client."
[LOG]: "ValidatorProxy caught an error."

In the second scenario, we’d like to intercept all methods of a class instead of a single one. This is a good fit for a package in which it’s desired to have all methods intercepted. For example, a service package that is responsible for communicating with a database or other services.

In this implementation, there won’t be any interfaces since our proxy is intended to intercept all methods. There’s will be a single super class though. The point here is that the super class has to iterate over its object’s methods in the constructor and replace each method with a proxy method. For this, we will make use of a default proxy implementation in ECMAScript that can be constructed by giving a target object and a handler. The target object here is a method and the handler is a custom object that contains an implementation for the apply method for calling functions. In a nutshell, we redefine subclasses objects prototypes.

abstract class ServiceProxy {    constructor() {        const objPrototypes = Object.getPrototypeOf(this);
const objPropertyNames = Object.getOwnPropertyNames(objPrototypes);
for (const index in objPropertyNames) { if(objPropertyNames[index] !== undefined) { const protoName = objPropertyNames[index];
const originalFunction = objPrototypes[protoName];
if (!Object.prototype.hasOwnProperty.call(objPrototypes, protoName)
|| originalFunction === undefined
|| originalFunction === null
|| typeof (originalFunction) !== 'function'
|| protoName === 'constructor') continue;
objPrototypes[protoName] = new Proxy(originalFunction, { apply(target, thisArg, argArray) {
try {
console.log("Proxy is calling an unknown method.");
return Reflect.apply(target, thisArg, argArray);
} catch(error) { console.log("ServiceProxy caught an error."); }
}
});
Object.setPrototypeOf(this, objPrototypes);
}
}
}
}

Whenever the code needs to instantiate a subclass of ServiceProxy, the superclass constructor will parse all methods and replace them with an instance of a proxy method that handles exceptions. In this approach, parameters are available in the argArray object and this is superb. Imagine there’s a request to your API that only breaks the code when certain parameter values are given and you want to log them or even trigger an alert to the entire team with the whole request object.

The following code is an example of its use and output.

class ClientService extends ServiceProxy {  getClients() {    console.log("Fetching clients from a database.");  }  saveClient() {    console.log("Saving client in the database.");    throw Error("There was a problem saving the client in the database.");  }
}
const clientService = new ClientService();
clientService.getClients();
clientService.saveClient();
[LOG]: "Proxy is calling an unknown method."
[LOG]: "Fetching clients from a database."
[LOG]: "Proxy is calling an unknown method."
[LOG]: "Saving client in the database."
[LOG]: "ServiceProxy caught an error."

Despite its simplicity, this a powerful feature to have in your application. For instance, if you’re writing a backend application, this allows you to make sure every error response will have the same structure or follow the same standards regardless the object that caused it. Besides that, it allows you to deal with different exceptions in the same code avoiding code replication. Not to mention other benefits like getting to log all actions, or the possibility of your team getting to know something went wrong in the exact moment it happened by triggering an e-mail message.

Thank you sticking with me this far in my first article. I intend to write about other topics related to backend applications and its ecosystem. Let me know if I can help you with something related to this theme.

--

--

Efraim Rodrigues
Efraim Rodrigues

No responses yet