Unobtrusive Dependency Injection with JavaScript
Why and how?
about:nfroidure
is hiring
Frontend / Backend developers
Data scientists
Embedded software engineers
Definition
The Dependency Injection pattern aims to provide the states some code depends on from some external code.
That way, the actual code is completely decoupled from its own dependencies implementation / initialization.
Dependency Injection on Wikipedia
In JS please
// With Classes
class User {
constructor(db, log) {
this.db = db;
this.log = log;
}
async delete() {
const result = await this.db.query('DELETE FROM users WHERE id = $id', { id });
if (result.deletedRows) {
this.log(`User ${id} has been deleted!`);
}
}
}
// With functions
async function deleteUser(db, log, id) {
const result = await db.query('DELETE FROM users WHERE id = $id', { id });
if (result.deletedRows) {
log(`User ${id} has been deleted!`);
}
}
App Lifecycle 101
Dependency Injection flow
Opinion
The Dependency Injection pattern is the only one you will never regret to implement.
If you do it with OOP do not blame DI.
Why?
Testable
Easy unit testing thanks to mocks and stubs.
Configurable
One can easily replace a dependency per another with no effort.
Manageable
Reasoning on limited scopes helps decreasing the cognitive load.
Reusable
By being more independent and modular, services can easily be reused in different contexts.
Optimal
By automating the process initialization boilerplate, the injector guarantees best performances and reliability (parallelization, no dead lock).
Instrumentable
Since well defined and isolated, an application using DI is extremely customizable and maleable.
Any downsides?
Debugging / tracing
It is simpler to debug a code that instantiate its own dependencies.
Static Typing
The injector creates dependencies dynamically so some advantages of static typing may fall apart.
Hands on!
Creating a simple CLI app from scratch
The problems
- Cannot reuse the db / FTP services
- Testing would be a pain (mockery)
- Cannot replace FTP per S3 easily
- Any new service requires a refactoring
of the boot sequence
⚠ Self Promotion ⚠
Knifecycle
The Dependency Injection banana without the gorilla nor the jungle and the rest of the world.
Refactoring
- Declare dependencies
- Instantiate the injector
- Run it with root dependencies
Alternatives
If you really want the gorilla, the jungle and the rest of the world, you may like to use the container-ioc library or the Angular and Nest frameworks that integrates their own DI systems. They fully rely on OOP and decorators fancy.
Going further
Autoload dependencies
Avoid boilerplate by automating the way your dependencies are loaded.
Generate graphs
Source: jsarch
Create a static build
Knifecycle offers the ability to create static builds. This is how we're bundling our serverless functions at Sencrop.
Creating a static build solves the DI downsides we saw earlier.
Build HTTP servers
Knifecycle were initially built for building HTTP servers and led to the Swagger HTTP Router project soon packaged in the Whook framework.
Ready to use services
- common-services
- pg-service
- jwt-service
- and more to come...
Built with Knifecycle
- metapak
- jsarch
- cloud-pages
- all my backends ;-)
Thanks! Questions?
Changes done
Summary of changes done during the talk: