Managing node modules and files. Part II

In my last post I discussed how I had started building an application with different parts that were all broken in to SRP files that exported a value, and thus were modules.  I discussed how I refactored this application so that each part was an autonomous module, with it’s own repo, package.json and entry point.

To be fair (to myself), my over all app was made up of several autonomous parts, but each part had several sub parts. For instance maybe one part connected to two different databases.  Both connections were used in the same app.  Separate file/modules and such but not in separate repos with their own package.json.

Ok, moving on I also spoke about how backlash against the idea of a Dependency Container was one reason I did this refactor.  Now I’d like to speak about the differences and similarities from a IOC perspective.

Some modules need to be configured, whether they are in their own repo or just another file.  For instance a database needs a connection string, your logger needs a level set and perhaps some application variables, a reusable module might need some information about what sort of entities it’s working with.  This is state, I guess, and what I have heard often is that modules should be stateless.  Most of my modules are stateless, but you can’t get around the fact that your application needs an input and an output.  If this is a web server then that is nice and tidy at the top level, but for worker modules and these are often a database or a queue and environment specific.

So what ends up happening is that you have some config values set in the top consuming app and passed to the modules that need them.  However, in the case of the database connection, this module is often rather deep.  The only way to get the values to their target is to require all your dependencies that need to be configured at the top level app, configure them and the pass them in as parameters to the dependencies that need them.  This is IOC.  Back in the C# world it’s what we called the poor man’s IOC.  The end result for one of my application services looks like this

var _eventDispatcher = require('eventDispatcher');
var _readStoreRepository = require('readStoreRepository');
var _eventHandlerBase = require('eventHandlerBase');
var _eventRepository = require('eventRepository');
var commandHandlers = require('./CommandHandlers/index');
var _eventStore = require('eventStore');
var yowlWrapper = require('yowlWrapper');
var extend = require('extend');
var config = require('config');

module.exports = function(_options){
    var options = config.get('myOptions');
    extend(options, _options || {});

    var logger = yowlWrapper(options.logger);
    var eventStore = _eventStore({eventstore:options.eventstore});
    var readStoreRepository = _readStoreRepository(
        {postgres: options.postgres});

    var eventRepository = _eventRepository(eventStore);
    var eventHandlerBase = _eventHandlerBase(eventStore,
                                             readStoreRepository);

    var handlers = commandHandlers(eventHandlerBase,
                                   eventRepository,
                                   readStoreRepository,
                                   logger);

    var eventDispatcher = _eventDispatcher(handlers, 
                                      eventStore,
                                     {targetStreamType:'command'});
    eventDispatcher.startDispatching();
};

As you can see the eventStore and the readStore are the bottom level modules but they are the ones that need the configuration.  They are then handed to the mid level modules, the repository and the handlers, which are in turn handed to the top level module all instantiated configured an ready to go.

In contrast, with my IOC Container my set up looked like this

module.exports = function(gesDispatcher, commandHandlers, logger) {
    return function () {
        logger.debug('instantiating dispatcher');
        var dispatcher = new gesDispatcher({
            targetType: 'command',
            handlers: commandHandlers.map(x=>new x())
        });
        dispatcher.startDispatching();
    }
};

It was pointed out that I should really show the config form the container for a proper comparison.  So here is the bootstrapper from my old code.

var _container = require('dagon');
var config = require('config');
module.exports = new _container(x=>
    x.pathToRoot(__dirname)
        .requireDirectoryRecursively('./src')
        .groupAllInDirectory('./src/CommandHandlers',
                              'commandHandlers')
        .for('logger').require('YowlWrapper')
        .for('logger')
             .instantiate(x=>x.asFunc()
                             .withParameters(config.get('logger')))
        .for('gesConnection')
              .instantiate(x=>x.asFunc()
                        .withParameters(config.get('eventStore')))
        .for('readModelRepository')
              .instantiate(x=>x.asFunc()
                        .withParameters(config.get('postgres')))
        .rename('lodash').withThis('_')
        .rename('bluebird').withThis('Promise')
        .complete());

This would not currently work with dagon and my new module pattern.  It worked the way I had it before.  Plus it could be a bit more elegant than requiring the config module.  But dagon is on my back burner right now.  I like the new stuff I’m learning and I may as well let it all shake out before I start thinking about making it better.

Poor mans IOC sounds … a bit pejorative I guess.  But in node with very small and autonomous pieces it’s not nearly that bad.  In fact, you could argue that it’s more explicit.  I can now accomplish all the things that I found so hard to do before.   The things that a container wrapped up and did for me.

So my ultimate ( as of this time 🙂 ) conclusion is this:  First, I was composing my modules poorly.  This led me to feel that I was unable to access pieces that I needed to configure dynamic variables.  Second, that by doing this manual IOC I could configure everything explicitly and stub things out if necessary during testing with out digging way down into it.  And third, that, while I had started this post as an explanation for why I don’t need a container, in fact, I think that a container does make things bit cleaner.

Boom, there, I said it.  Flip flopped right in my own posting! Still the exploration has lead to a nice pattern of making SRP modules explicit by breaking them out into there own repos and composing them more clearly.  I also feel that I could very easily use a container or manual IOC and still develop an application with the ability to access and substitute modules at runtime.

Advertisements
Managing node modules and files. Part II

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s