This project contains:
- a Slidev presentation in
slides.md - a runnable Vanilla JS DI/IoC demo in
demo/
This repository demonstrates a metadata-driven Dependency Injection (DI) and Inversion of Control (IoC) design in plain JavaScript, using decorators as the annotation mechanism and a small runtime container as the resolver.
- Keep architecture framework-independent (no framework DI runtime).
- Preserve explicit composition at startup (
register+resolve). - Use current standards where possible (Decorators Stage 3).
- Isolate experimental syntax (parameter decorators) from production baseline.
The runtime architecture is split into four layers:
- Service classes (
demo/services.js) - Metadata decorator (
demo/decorators.js) - IoC container (
demo/container.js) - Composition root (
demo/main.js)
The composition root registers tokens/providers and resolves the top-level app type. The container recursively resolves dependencies from metadata and builds the object graph.
Container is intentionally minimal and usually exposes:
register(token, provider, options)to map abstract tokens to concrete providers.resolve(token)to recursively build an instance graph.- singleton/transient lifecycle handling through registration options.
Internally, the container keeps:
- a provider registry (token -> provider + config)
- an instance cache (for singleton reuse)
- a recursive resolution path
Resolution algorithm (high level):
- Look up token in registry.
- If singleton and cached, return cached instance.
- Read dependency metadata from provider class.
- Resolve each dependency token recursively.
- Instantiate provider with resolved dependencies.
- Cache if singleton, then return instance.
The Injectable decorator attaches dependency metadata to classes instead of
hardcoding new calls inside classes. This preserves DI principles:
- classes declare what they need
- wiring stays centralized in the composition root
- tests can swap tokens/providers without rewriting service internals
In practical terms, decorator metadata is the contract between class declarations and container resolution.
- Small runtime surface area (easy to audit and debug).
- No hidden framework lifecycle assumptions.
- Works with standard JavaScript execution model.
- Supports incremental growth (interceptors, scoped lifetimes, factories).
demo/experimental-parameter-decorators.js shows a potential future API where
constructor parameters can be directly annotated for injection intent. This is
currently an ergonomics preview only, not the production baseline.
Production path today:
- class-level decorators and explicit metadata
- explicit registration at app startup
- deterministic container resolution
slides.mdexplains the narrative, tradeoffs, and standards status.snippets/mirrors key code used in slides so the talk stays synchronized with runnable examples.
- Node.js 18+ (Node.js 20+ recommended)
- npm
npm installnpm run devSlidev will print a local URL (usually http://localhost:3030).
npm run buildBuild output is generated in dist/.
npm run exportnode demo/main.jsExpected output:
[log] users: Ada Lovelace, Grace Hopper, Margaret Hamilton