Monday 18 April 2016

Supermarket approach to Dependency Injection

I'm working on a Node.js application that is slowly growing in size. I've been learning Node.js for the last 5 months, and am interested in design patterns and approaches that should make my code easier to maintain, easier to test, and simpler to code.One thing I've seen a lot of is Dependency Injection, both to help with a loosely coupled modules and specifically test mocking. Learning about these processes seems overwhelming and people start talking about factories, mixins and design rules, which due to the complexity of the solution are ok to break every now and then.As a quick background:I saw this video on Testable Javascript by Mark Trostler from Google, and it was great. He explains very well the challenges of architecting your application for testability, a great watch.A good pattern he shows is your application really has two phases, the 'creation' phase and the 'use' phase.During the creation phase, which only happens once, you build and initialize key components that your application will use. This is where dependency evaluation occurs and you are building a collection of objects you can later pass to functions to help them operate correctly.The issue I see, is when it comes to dependency injection, even when using libraries like cujoJS or your own DI model, is that the complexity of your application increases rapidly. This is predominantly to enforce the "Interface Segregation Principle" where "No object should be forced to depend on methods it does not use".The Supermarket approach to Dependency Injection:The idea I'm thinking about is a gross simplification of Dependency Injection, and my concern is my naivety and inexperience is fooling me into thinking it is a good idea, would love to hear your feedback.When your application is starting, during the 'creation' phase, you create an object that will hold references to all interfaces used by your application.Lets say your application had a UserController function which could use one of two UserRepo implementations, UserRepoRedis and UserRepoS3. During your creation phase you work out you want to use UserRepoRedis so you instantiate the function and add it to the Supermarket object at appSupermarket.UserRepo.Then when your UserController object initialises, it would be passed the appSupermarket object and initialize like this:var UserController = function(appSupermarket) { this.userRepo = appSupermarket.UserRepo; } ; That is a simple view, and you would test if appSupermarket.UserRepo was typeof 'function'.Over time, as your application grew, you would continue to build up the interfaces under appSupermarket, and just let functions pick and choose which functions and objects they want to pull out of there.From a testing point of view, it is very easy to mock up the appSupermarket object.It looks like it breaks the "Interface Segregation Principle" by giving functions access to many more functions than they need, but we aren't 'forcing them to depend on methods they do not use', rather we are offering more methods than they need, they can choose to ignore them.It would be possible to add some code at the beginning of a module to ensure it can find the required modules within appSupermarket that it needs defined, and if it finds they aren't there it can handle that error. This gives us some manageability over which module needs which module without getting too complex.Apologies for the wall of text. I sometimes think some programming patterns can get caught up in themselves and to me this seems to be something that can be done simpler. I just want to start off the conversation and see what others think.Edit: formatting

Submitted April 19, 2016 at 03:05AM by FourtyTwoBlades

No comments:

Post a Comment