universal-common-dependencyinjection
TypeScript icon, indicating that this package has built-in type declarations

1.0.0 • Public • Published

universal-common-dependencyinjection

A lightweight dependency injection library for JavaScript applications.

Installation

npm install universal-common-dependencyinjection

Overview

This library provides a simple, flexible dependency injection system for JavaScript applications:

  • ServiceCollection - Container for registering services with different lifetimes
  • ServiceProvider - Resolves registered services when needed in your application

Usage

Importing

import { ServiceCollection, ServiceProvider } from "universal-common-dependencyinjection";

Basic Service Registration

class Logger {
  log(message) {
    console.log(`[LOG]: ${message}`);
  }
}

class UserService {
  #logger;
  
  constructor(logger) {
    this.#logger = logger;
  }
  
  getUser(id) {
    this.#logger.log(`Getting user with ID: ${id}`);
    return { id, name: "John Doe" };
  }
}

// Create a service collection
const services = new ServiceCollection();

// Register services
services.addSingleton(Logger, () => new Logger());
services.addTransient(UserService, (serviceProvider) => {
  const logger = serviceProvider.getRequiredService(Logger);
  return new UserService(logger);
});

// Build the service provider
const serviceProvider = services.buildServiceProvider();

// Resolve and use services
const userService = serviceProvider.getRequiredService(UserService);
const user = userService.getUser(123);
console.log(user); // { id: 123, name: "John Doe" }

Singleton Services

Singleton services are created once and the same instance is returned for each request:

class DatabaseConnection {
  constructor() {
    console.log("Opening database connection...");
  }
  
  query(sql) {
    console.log(`Executing query: ${sql}`);
  }
}

const services = new ServiceCollection();

services.addSingleton(DatabaseConnection, () => new DatabaseConnection());

const serviceProvider = services.buildServiceProvider();

const db1 = serviceProvider.getService(DatabaseConnection);
const db2 = serviceProvider.getService(DatabaseConnection);

console.log(db1 === db2); // true

Transient Services

Transient services are created each time they are requested:

class RequestHandler {
  #requestId;
  
  constructor() {
    this.#requestId = Math.random().toString(36).substr(2, 9);
    console.log(`Created request handler: ${this.#requestId}`);
  }
  
  get requestId() {
    return this.#requestId;
  }
}

const services = new ServiceCollection();

services.addTransient(RequestHandler, () => new RequestHandler());

const serviceProvider = services.buildServiceProvider();

const handler1 = serviceProvider.getService(RequestHandler);
const handler2 = serviceProvider.getService(RequestHandler);

console.log(handler1 === handler2);
console.log(handler1.requestId);
console.log(handler2.requestId);

Pre-Built Instances

You can register pre-built instances as singletons:

class Configuration {
  #settings;
  
  constructor(settings) {
    this.#settings = settings;
  }
  
  getSetting(key) {
    return this.#settings[key];
  }
}

const config = new Configuration({
  apiUrl: "https://api.example.com",
  timeout: 5000
});

const services = new ServiceCollection();

// Register the pre-built instance
services.addSingleton(Configuration, config);

const serviceProvider = services.buildServiceProvider();

// The pre-built instance is returned
const resolvedConfig = serviceProvider.getService(Configuration);
console.log(resolvedConfig.getSetting("apiUrl")); // https://api.example.com

Resolving Optional Services

You can handle optional dependencies using getService():

class AnalyticsService {
  #logger;
  
  constructor(logger) {
    this.#logger = logger || {
      log: () => {}
    };
  }
  
  trackEvent(name, data) {
    this.#logger.log(`Tracking event: ${name}`);
    // Implementation
  }
}

const services = new ServiceCollection();
services.addTransient(AnalyticsService, (provider) => {
  const logger = provider.getService(Logger);
  return new AnalyticsService(logger);
});

const serviceProvider = services.buildServiceProvider();
const analytics = serviceProvider.getRequiredService(AnalyticsService);
analytics.trackEvent("page_view", { path: "/home" });

Service Dependencies

Services can depend on other services, which will be resolved automatically:

class AuthService {
  authenticate(credentials) {
    return { userId: 1, role: "admin" };
  }
}

class UserRepository {
  getById(id) {
    return { id, name: "John Doe" };
  }
}

class UserController {
  #authService;
  #userRepository;
  
  constructor(authService, userRepository) {
    this.#authService = authService;
    this.#userRepository = userRepository;
  }
  
  login(credentials) {
    const authResult = this.#authService.authenticate(credentials);
    if (authResult) {
      const user = this.#userRepository.getById(authResult.userId);
      return {
        user,
        token: "jwt-token-here"
      };
    }
    return null;
  }
}

const services = new ServiceCollection();

services.addSingleton(AuthService, () => new AuthService());
services.addSingleton(UserRepository, () => new UserRepository());

// UserController depends on AuthService and UserRepository
services.addTransient(UserController, (provider) => {
  const authService = provider.getRequiredService(AuthService);
  const userRepository = provider.getRequiredService(UserRepository);
  return new UserController(authService, userRepository);
});

const serviceProvider = services.buildServiceProvider();
const userController = serviceProvider.getRequiredService(UserController);

const loginResult = userController.login({ username: "john", password: "secret" });
console.log(loginResult);

Package Sidebar

Install

npm i universal-common-dependencyinjection

Weekly Downloads

1

Version

1.0.0

License

CC0-1.0

Unpacked Size

21.3 kB

Total Files

8

Last publish

Collaborators

  • ong.andrew