Introspec
Dependencies and configuration described through data.
npm install introspec
Why even?
Introspec is a variation of Integrant for JavaScript. A great talk by the author of the framework goes into the details of why it simplifies system structure.
Re-iterating one main point here:
Code obscures structure
Component systems often leave dependency injection to the code. For example, when component A needs component B to do its job.
{} {} const b = username: 'bender' password: 'antiquing' const a = bot: b debug: false bug: true a
The problem is that this dependency carries a lot of overhead configuring A. We know need to know the invocation of B, the instantiation order, what options both A and B need. Some of this is avoidable, but consider if we put this into a data structure instead.
a: bot: // ref points to a top key in the same config debug: false bug: true b: username: 'bender' password: 'antiquing'
The data structure isolates the dependencies and configuration. The invocation happens, unavoidably, in the code. We do stop caring how some B gets to some A. The other benefit is we can query our configuration in one place to get values like b.username
.
Documentation
Introspec has a three function API surface.
ref
ref(key: string)
returns an internal symbol for pointing to a top-level configuration value. This is the function that applies to building configurations.
start
-
start(key: string, startFn: (options: map) -> Promise<service: any>)
assigns a startup hook to the top-level config property with the givenkey
which calls with its instantiatedoptions
when that property resolves for usage. -
start(config: object, entryPoints: Array<string>) completion: Promise<system>
resolves the dependency graph for top-levelentryPoints
, applying the startup hooks for the respective dependencies, and returns a promise which resolves with a startedsystem
reference.
stop
-
stop(key: string, stopFn: (service: any) -> Promise<_: any>)
assigns a shutdown hook to the top-level config property with the givenkey
which calls with the startup result when that property resolves for destruction. -
stop(system) completion: Promise
shuts down thesystem
starting with the top-levelentryPoints
, applying the shutdown hooks for the respective dependencies, and returns a promise which resolves on completion.
Non-global Introspecs
Introspec exports a default instance for convenience. Make Introspec instances with their own lifecycle hook registries and nest them at will.
const myIntrospec = myIntrospecmyIntrospecstart b: 'cake' 'b'
Introspec IRL
For larger projects, this data structure based dependency graph shines.
Imagine the following application:
- Five services: a HTTP(S) webserver, Redis connection, Postgres connection, email client, and payments API.
- The webserver uses the four other services handling requests.
- The email client needs Redis for storing email link tokens.
- The payments API needs both Redis and Postgres for persistence.
/* Environment variables and intermediate transformations are fine. Configs are plain data structures after all.*/const config = webserver: port: 8080 cache: database: email: payments: redis: 'redis://...' postgres: 'postgres://...' email: from: 'introspec@example.com' redis: payments: apiKey: 'xkcd12b4ucab' cache: database: /* Start/stop hooks can fit anywhere, preferably with the relevant code (which I was too lazy to mock here).*/
Contributing
Contributions are incredibly welcome as long as they are standardly applicable and pass the tests (or break bad ones). Tests are in AVA.
# running tests npm run test
Follow me on Twitter for updates or for the lolz and please check out my other repositories if I have earned it. I thank you for reading.