Git Product home page Git Product logo

Comments (2)

nfroidure avatar nfroidure commented on September 22, 2024

This is a perfectly valid approach to get things done quickly. I used to work like this for the SimpliField back end, we used to call it the context object which is kinda similar to the example you provided.

That said, it has downsides that a DI system can resolve.

An HTTP server depends on the database. If this is the only dependency of your code, the context object may be enough. But when you have several databases/connections to manage (a message broker, several databases, IPC, WebSocket ...) it quickly becomes a great mess.

You keep repeating yourself by creating startup and shutdown code depending on inter-relations of those services. Code that you have to test/maintain/debug. It simply doesn't scale and there is a point where it becomes error prone.

With a DI system based on Inversion of Control, you simply declare dependencies and things gets up with no extra work.

The fact you express those dependencies statically also have nice side effects.

  • lazy service instantiation/shutdown: you may open a DB connection only when needed. With the context object approach you have to do it once for all, every services are singletons.
  • easy service swapping: with the context object approach you hard set dependencies so that if you need two instances of the same service at some point, you have a whole bunch of instantiation/shutdown code to change.
  • Dependency graphs out of the box: You may do it with the require system too but you graph would be much complexer since the pure libraries would sit in it.
  • composable webservices: I can easily create web services by composing the endpoints I need. That way, if it needs a database it will be instantiated, but if, by chance the composed webservice never need a db connection, it will just be ignored and consume needed resources only.
  • strong dependencies: with the context object approach services/controllers are not tied with the actual services they depends on. The context object is like a god object that may or may not contain the service you need. It may take some time to debug situations like a service rename/split. With a DI system the code break and you just have to fix it up.
  • fatal errors handling: if a required service goes wrong (typically a connection broke) you can handle it simply and properly crash the process. Handling such a situation by hand and gracefully shutdown is typically complex and error prone if you have to do it by hand.

That said, I agree with you on the statement that a DI approach may not be the good approach especially for small/PoC/MVP backends. This is why I made efforts to fit with the context object approach so that you may just not use knifecycle and til use services that where primarily build for it. Basically, a knifecycle service is a function taking the context object as a first parameter and returning a promise of the actual service instance. A provider is a bit more complex since it returns a promise of an object with the actual service instance but also has hooks to shut it down and listen to fatal errors.

It has the nice side effect to also help reuse service that were primarily designed for the context object approach. Using your get GetTimeRoute.js file in my apps would simply looks like this:

const GetTimeRoute = require('./GetTimeRoute')

// (....)
$.service('time', $.depends(['now', 'logger'], GetTimeRoute));

// let's say someday I need it but with a distant timer:
$.service('time', $.depends(['now:remoteNow', 'logger'], GetTimeRoute));

I hope I answered well to your wonderings, if not, let me know I'd be happy to give your ore input.

Edit: There are some choices I am not happy with, for example, dependencies declarations. The 2th version should be better on that particular matter and improve the compatibility with the context object approach #24

from knifecycle.

nfroidure avatar nfroidure commented on September 22, 2024

Worth mentioning, the problem has already been discussed in Stack Overflow. The chosen answer tends to the context method while the next prone DI. http://stackoverflow.com/questions/9250851/do-i-need-dependency-injection-in-nodejs-or-how-to-deal-with

Both has valid arguments, in a way, the Knifecycle approach reconcile them by not tiding code to it and allowing using the both approach without changing your services implementation.

Closing since it is clear that trade-off exists for both approaches and it is a matter of taste.

from knifecycle.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.