NovaDI Examples

Interactive demonstrations of NovaDI patterns and usage

TypeScript 5+ Zero Decorators TS Transformer Browser-First Type-Safe

📋 About These Examples

This page showcases NovaDI usage patterns through interactive examples that demonstrate different DI features and patterns. Open your browser console (F12) to see detailed execution logs with grouped output showing how dependencies are resolved and services interact.

✨ TypeScript Transformer: NovaDI includes a compile-time transformer that automatically injects type names into .as<T>() calls, eliminating the need for string literals while maintaining full type safety. Write clean, intuitive code: .as<ILogger>() instead of .as<ILogger>("ILogger").

⚡ Performance: For performance benchmarks comparing NovaDI with other DI frameworks, see the Benchmarks page.

📖 Static Examples: All examples below are shown with expanded code snippets. Click the headers to collapse/expand sections.

🌟 Example 1: Simplest Case

The absolute simplest usage - just .as<T>() and .resolveType<T>()!

  • No Token<T>() needed - transformer injects type names
  • No .autoWire() configuration needed
  • Clean, intuitive API

💻 Code Example

// Create container
const container = new Container()
const builder = container.builder()

// Register services without any dependencies
builder.registerType(ConsoleLogger).as<ILogger>().singleInstance()
builder.registerType(EventBus).as<IEventBus>().singleInstance()

const app = builder.build()

// Resolve services
const logger = app.resolveType<ILogger>()
const eventBus = app.resolveType<IEventBus>()

// Use them
logger.info('Simple registration works!', 'Demo0')
eventBus.publish('demo:started', { demo: 0 })

📦 Example 2: Basic DI with Autowiring

Dependency injection with autowiring - automatic dependency resolution.

  • Interface registration
  • Autowired dependencies
  • Singleton lifetime

💻 Code Example

const container = new Container()
const builder = container.builder()

// Register logger first
builder.registerType(ConsoleLogger).as<ILogger>().singleInstance()

// Register EventBus with autowired logger dependency
builder
  .registerType(EventBus)
  .as<IEventBus>()
  .autoWire({
    map: {
      logger: (c) => c.resolveType<ILogger>()
    }
  })
  .singleInstance()

const app = builder.build()

// Resolve dependencies
const logger = app.resolveType<ILogger>()
const eventBus = app.resolveType<IEventBus>()

logger.info('Smart Home system initialized', 'Demo1')
eventBus.publish('system:ready', { timestamp: Date.now() })

🔀 Example 3: Multiple Implementations

Register multiple implementations of the same interface using keyed services.

  • Keyed services
  • resolveTypeKeyed()
  • resolveTypeAll()

💻 Code Example

const builder = new Container().builder()

// Register multiple logger implementations
builder.registerType(ConsoleLogger).as<ILogger>() // Default
builder.registerType(FileLogger).as<ILogger>().keyed('file')

const app = builder.build()

// Resolve default and keyed loggers
const defaultLogger = app.resolveType<ILogger>()
const fileLogger = app.resolveTypeKeyed<ILogger>('file')
const allLoggers = app.resolveTypeAll<ILogger>()

console.log('Default logger:', defaultLogger.constructor.name)
console.log('File logger (keyed):', fileLogger.constructor.name)
console.log('All loggers:', allLoggers.map(l => l.constructor.name).join(', '))

defaultLogger.log('Logging to console', 'Demo2')
fileLogger.log('Logging to file', 'Demo2')

🔧 Example 4: Autowired Dependencies

Automatic dependency resolution through constructor injection.

  • Constructor injection
  • Dependency graphs
  • Type safety

💻 Code Example

const builder = new Container().builder()

// Register with dependencies
builder.registerType(ConsoleLogger).as<ILogger>().singleInstance()

builder
  .registerInstance(new TemperatureSensor('temp-1', 'Bedroom Temp', 'bedroom', 21))
  .as<ISensor>()

builder
  .registerType(SmartLight)
  .as<IDevice>()
  .autoWire({
    map: {
      id: () => 'light-1',
      name: () => 'Bedroom Light',
      roomId: () => 'bedroom',
      logger: (c) => c.resolveType<ILogger>()
    }
  })

const app = builder.build()

const sensor = app.resolveType<ISensor>()
const light = app.resolveType<IDevice>()

console.log(`Temperature: ${sensor.getValue()}${sensor.getUnit()}`)
light.turnOn()

🏠 Example 5: Scoped Containers

Isolated dependency scopes using child containers (per-room isolation).

  • Child containers
  • Scope isolation
  • Shared singletons

💻 Code Example

// Create parent container with shared services
const parentBuilder = new Container().builder()
parentBuilder.registerType(ConsoleLogger).as<ILogger>().singleInstance()
const parentContainer = parentBuilder.build()

// Create room-specific child containers
const livingRoomBuilder = parentContainer.createChild().builder()
livingRoomBuilder.registerInstance(
  new TemperatureSensor('lr-temp', 'Living Room Temp', 'living-room', 23)
).as<ISensor>()
const livingRoomContainer = livingRoomBuilder.build()

const bedroomBuilder = parentContainer.createChild().builder()
bedroomBuilder.registerInstance(
  new TemperatureSensor('br-temp', 'Bedroom Temp', 'bedroom', 19)
).as<ISensor>()
const bedroomContainer = bedroomBuilder.build()

// Resolve from each scope
const lrSensor = livingRoomContainer.resolveType<ISensor>()
const brSensor = bedroomContainer.resolveType<ISensor>()
const lrLogger = livingRoomContainer.resolveType<ILogger>()
const brLogger = bedroomContainer.resolveType<ILogger>()

console.log(`Living Room temp: ${lrSensor.getValue()}°C`)
console.log(`Bedroom temp: ${brSensor.getValue()}°C`)
console.log('Both rooms share the same logger:', lrLogger === brLogger)

🤖 Example 6: Complex Automation System

Complete real-world application with complex dependency graphs and automation rules.

  • Multi-layer architecture
  • Event-driven system
  • Multiple keyed services
  • Complex dependency graph

💻 Code Example

const builder = new Container().builder()

// Core services
builder.registerType(ConsoleLogger).as<ILogger>().singleInstance()
builder
  .registerType(EventBus)
  .as<IEventBus>()
  .autoWire({ map: { logger: (c) => c.resolveType<ILogger>() } })
  .singleInstance()

builder
  .registerType(AutomationService)
  .as<AutomationService>()
  .autoWire({
    map: {
      logger: (c) => c.resolveType<ILogger>(),
      eventBus: (c) => c.resolveType<IEventBus>()
    }
  })
  .singleInstance()

// Sensors (keyed registration for multiple sensors)
builder
  .registerInstance(new TemperatureSensor('auto-temp', 'Auto Temp', 'office', 22))
  .as<ISensor>()
  .keyed('TempSensor')

builder
  .registerInstance(new MotionSensor('auto-motion', 'Auto Motion', 'office'))
  .as<ISensor>()
  .keyed('MotionSensor')

// Devices (keyed registration for multiple devices)
builder
  .registerType(SmartThermostat)
  .as<IDevice>()
  .keyed('Thermostat')
  .autoWire({
    map: {
      id: () => 'auto-thermo',
      name: () => 'Auto Thermostat',
      roomId: () => 'office',
      logger: (c) => c.resolveType<ILogger>()
    }
  })

const app = builder.build()

// Resolve all components
const automationService = app.resolveType<AutomationService>()
const tempSensor = app.resolveTypeKeyed<ISensor>('TempSensor')
const thermostat = app.resolveTypeKeyed<IDevice>('Thermostat')

// Create and register automation rules
const tempRule = new TemperatureAutomationRule(
  'temp-rule-1',
  'Temperature Control',
  tempSensor,
  thermostat,
  24,
  logger,
  eventBus
)
automationService.addRule(tempRule)
automationService.start()