Skip to main content

Best Practices

This guide presents the best practices for using @brushy/di efficiently and in an organized manner. For package choice and framework setup (Express, Next.js, React Native), start with Getting Started.

Token Organization

Use Symbols for Tokens

Prefer createToken("…") or Symbol("…"). Never use plain strings. Strings can collide when two modules use the same name; createToken returns Symbol(description) at runtime, so every token is unique.

Use createToken for Type Inference

createToken is the recommended form of Symbol token: same collision safety, plus inference in register, resolve, useInject, factory dependencies, and useInjectComponent when registering with useValue:

Centralize Token Definition

Keep all tokens in a centralized location:

Lifecycle

Choose the Appropriate Lifecycle

  • Use singleton for shared services (default)
  • Use transient for instances that should not be shared
  • Use scoped for instances that should be shared within a scope (e.g., HTTP request)
  • Use immutable for state managers and instances that should never be invalidated

Use Immutable Lifecycle for State Managers

When working with state management libraries like React Query, Redux, or Zustand, use the immutable lifecycle to ensure the instance is never invalidated:

Clean Up Resources Properly

Application Structure

Independent Modules

Organize your application into independent modules, each with its own container:

Injection in React Components

Prefer using hooks for injection in React components:

Component injection (UI)

Register swappable UI on the same container as services. Prefer new Container({ providers: [{ provide, useValue }] }) or container.register(createToken("…"), { useValue: Component }). Resolve in the shell with useInjectComponent:
Explicit createToken<T>() is optional. Use it only when the token contract must exist before the implementation (shared tokens.ts). See Component Injection.

Performance

Promise Caching

Use promise caching to avoid multiple API calls:

Lazy Loading

Use useInjectLazy to load heavy dependencies only when needed:

Testability

Test Containers

Create specific containers for tests:

Easy Mocking

Use useValue to inject mocks in tests:

Observability

Monitor the Container

Use the monitor to debug issues:

Verify Immutable Integrity

Use the verifyImmutableIntegrity method to ensure immutable instances maintain their identity:

Detailed Logging

Enable debug mode for detailed logging:

Security

Validation of Dependencies

Validate dependencies when registering them:

Avoid Exposing Sensitive Services

Don’t expose sensitive services directly:

Complete Architecture Example

tokens.ts

container.ts

AppProvider.tsx

useAuth.ts