From the preceding discussion, it seems like dependency injection is the better choice. Readability should not be too much of an issue, as we only have two layers of abstraction – handlers and engines. Therefore, let's migrate our code to use the dependency injection pattern.
First, remove the import statements from src/handlers/users/create/index.js and change the signature of the createUser function to include the create engine function and the ValidationError class:
function createUser(req, res, db, create, ValidationError) { ... }
Now, we need to inject these dependencies into the handler. In src/index.js, we are already using the injectHandlerDependencies function to inject the database client into the handler, so let's modify it to also inject the corresponding engine function and ValidationError class.
First, let's import all the dependencies inside src/index.js:
import ValidationError from './validators/errors/validation-error';
import createUserHandler from './handlers/users/create';
import createUserEngine from './engines/users/create';
Next, let's create a mapping of handler functions to engine functions, and call it handlerToEngineMap. We will pass this handlerToEngineMap function into the injectHandlerDependencies function, so that it knows which engine to inject:
const handlerToEngineMap = new Map([
[createUserHandler, createUserEngine],
]);
We are using the Map object, which was introduced in ECMAScript 2015 (ES6). A Map is a key-value store, where the keys and values can be of any type – primitives, objects, arrays, or functions (the last two are just special types of object). This is unlike an object literal, where the keys must be either a string or a Symbol. Here, we are storing the handler function as the key, and the engine function as the value.
All that's left to do in src/index.js is to add handlerToEngineMap and ValidationError into injectHandlerDependencies:
app.post('/users', injectHandlerDependencies(createUserHandler, client, handlerToEngineMap, ValidationError));
Finally, update the injectHandlerDependencies function to relay these dependencies into the handler:
function injectHandlerDependencies(handler, db, handlerToEngineMap, ValidationError) {
const engine = handlerToEngineMap.get(handler);
return (req, res) => { handler(req, res, db, engine, ValidationError); };
}
We've made a lot of changes in many files, so you should run all of our existing tests again to make sure that we didn't break anything. You may also want to commit these changes to the Git repository:
$ git add -A && git commit -m "Implement dependency injection pattern"