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()

🔌 Example 7: Plugin System with Array Injection

NEW! Automatically inject ALL implementations of an interface using array parameters. Perfect for plugins, event handlers, and middleware.

  • Automatic array injection
  • Multiple plugin implementations
  • Zero manual configuration
  • Transformer-powered

💻 Code Example

// Define plugin interface
interface IPlugin {
  name: string
  execute(): string
}

// Create multiple plugin implementations
class ValidationPlugin implements IPlugin {
  name = 'validation'
  execute(): string {
    console.log('Validating...')
    return 'validated'
  }
}

class AuthPlugin implements IPlugin {
  name = 'auth'
  execute(): string {
    console.log('Authenticating...')
    return 'authenticated'
  }
}

class LoggingPlugin implements IPlugin {
  name = 'logging'
  execute(): string {
    console.log('Logging...')
    return 'logged'
  }
}

// Plugin host with array parameter - transformer auto-detects!
class PluginHost {
  constructor(public plugins: IPlugin[]) {}  // ✨ Array injection

  executeAll(): string[] {
    console.log(`Executing ${this.plugins.length} plugins...`)
    return this.plugins.map(p => {
      console.log(`  - ${p.name}`)
      return p.execute()
    })
  }

  getPluginNames(): string[] {
    return this.plugins.map(p => p.name)
  }
}

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

// Register all plugins
builder.registerType(ValidationPlugin).as<IPlugin>()
builder.registerType(AuthPlugin).as<IPlugin>()
builder.registerType(LoggingPlugin).as<IPlugin>()

// Register host - transformer automatically generates resolveTypeAll()!
builder.registerType(PluginHost).as<PluginHost>()

// Build and resolve
const app = builder.build()
const host = app.resolveType<PluginHost>()

// Use the plugin system
console.log('Registered plugins:', host.getPluginNames())
// Output: ['validation', 'auth', 'logging']

const results = host.executeAll()
// Output:
//   Executing 3 plugins...
//     - validation
//     - auth
//     - logging

console.log('Results:', results)
// Output: ['validated', 'authenticated', 'logged']

🔍 What the Transformer Generates

// Before transformation:
builder.registerType(PluginHost).as<PluginHost>()

// After transformation (automatic!):
builder.registerType(PluginHost).as<PluginHost>().autoWire({
  mapResolvers: [
    (c) => c.resolveTypeAll("IPlugin")  // 🎯 Auto-generated!
  ]
})

// Supported array syntaxes:
// ✅ IPlugin[]
// ✅ Array<IPlugin>
// ✅ readonly IPlugin[]

📦 Use Cases

  • 🔌 Plugin systems - Load all plugins dynamically
  • 📨 Event handlers - All subscribers to same event
  • 🔗 Middleware - Execute through all middleware
  • Validators - Run all validation rules
  • 📡 Notifications - Send to all channels