Skip to main content

Dependency Injection Best Practices

This document provides best practices for using dependency injection with Contexify.

Use Decorators for Dependency Injection

It's recommended to use decorators for dependency injection instead of directly retrieving dependencies from the Context:

import { inject, injectable } from 'contexify';

@injectable()
export class UserController {
constructor(
@inject('repositories.UserRepository') private userRepo: UserRepository,
@inject('services.EmailService') private emailService: EmailService
) {}

async createUser(userData: UserData) {
const user = await this.userRepo.create(userData);
await this.emailService.sendWelcomeEmail(user);
return user;
}
}

Benefits:

  • Dependencies are explicit and visible
  • Easy to test, as dependencies can be mocked
  • Code is cleaner and more maintainable

Binding Key Naming Conventions

Use consistent naming conventions to organize binding keys:

// Services
app.bind('services.EmailService').toClass(EmailService);

// Repositories
app.bind('repositories.UserRepository').toClass(UserRepository);

// Controllers
app.bind('controllers.UserController').toClass(UserController);

// Configuration
app.bind('config.api').to({
port: 3000,
host: 'localhost',
});

Recommended naming patterns:

  • {namespace}.{name}: Use namespace and name
  • Use plural forms for namespaces (services, repositories, controllers)
  • For configurations, use config.{component}

Scope Management

Choose appropriate scopes based on the nature of the component:

import { BindingScope } from 'contexify';

// Singleton service
app
.bind('services.ConfigService')
.toClass(ConfigService)
.inScope(BindingScope.SINGLETON);

// One instance per request
app
.bind('controllers.RequestController')
.toClass(RequestController)
.inScope(BindingScope.TRANSIENT);

// Singleton in the current context
app
.bind('services.CacheService')
.toClass(CacheService)
.inScope(BindingScope.CONTEXT);

Scope guidelines:

  • SINGLETON: For services with shared state (configurations, database connections)
  • TRANSIENT: For components that need a new instance each time they're used
  • CONTEXT: For components shared within a specific context