For the complete documentation index, see llms.txt. This page is also available as Markdown.

Visual Diagrams

Visual explanations of Aurelia 2's dependency injection system.

Table of Contents


1. Container Hierarchy and Resolution

How containers inherit and override registrations:

ROOT CONTAINER (Application Level)
══════════════════════════════════════════════════════

┌────────────────────────────────────────────────┐
│ Root Container                                 │
│                                                │
│ Registrations:                                 │
│  • ILogger → ConsoleLogger (singleton)        │
│  • IAppConfig → {...} (instance)              │
│  • IApiClient → ApiClient (singleton)         │
└────────────────────┬───────────────────────────┘

          ┌──────────┴──────────┐
          ↓                     ↓
┌─────────────────────┐ ┌──────────────────────┐
│ Feature Container 1 │ │ Feature Container 2  │
│ (Admin Module)      │ │ (User Module)        │
│                     │ │                      │
│ Inherits:           │ │ Inherits:            │
│  • ILogger ✓        │ │  • ILogger ✓         │
│  • IAppConfig ✓     │ │  • IAppConfig ✓      │
│  • IApiClient ✓     │ │  • IApiClient ✓      │
│                     │ │                      │
│ Overrides:          │ │ Adds:                │
│  • ILogger →        │ │  • IUserService →    │
│    AdminLogger      │ │    UserService       │
│                     │ │  • IAuthGuard →      │
│ Adds:               │ │    UserAuthGuard     │
│  • IAdminService →  │ │                      │
│    AdminService     │ │                      │
└─────────────────────┘ └──────────────────────┘
          ↓                     ↓
     ┌─────────┐          ┌──────────┐
     │Component│          │Component │
     │  asks   │          │  asks    │
     │  for    │          │  for     │
     │ ILogger │          │ ILogger  │
     └────┬────┘          └────┬─────┘
          │                    │
          ↓                    ↓
    AdminLogger          ConsoleLogger
  (from Feature 1)        (from Root)


RESOLUTION ALGORITHM
════════════════════════════════════════════════════════

Component requests: IUserService

Step 1: Check current container
┌────────────────────────────────────┐
│ Current: Feature Container 2       │
│ Has IUserService? YES ✓            │
│ → Return UserService instance      │
└────────────────────────────────────┘
         Resolution complete!


Component requests: IApiClient

Step 1: Check current container
┌────────────────────────────────────┐
│ Current: Feature Container 2       │
│ Has IApiClient? NO ✗               │
└──────────────┬─────────────────────┘

Step 2: Check parent container
┌────────────────────────────────────┐
│ Parent: Root Container             │
│ Has IApiClient? YES ✓              │
│ → Return ApiClient instance        │
└────────────────────────────────────┘
         Resolution complete!


Component requests: IUnknownService

Step 1: Check current container
┌────────────────────────────────────┐
│ Current: Feature Container         │
│ Has IUnknownService? NO ✗          │
└──────────────┬─────────────────────┘

Step 2: Check parent container
┌────────────────────────────────────┐
│ Parent: Root Container             │
│ Has IUnknownService? NO ✗          │
└──────────────┬─────────────────────┘

Step 3: No more parents
┌────────────────────────────────────┐
│ ❌ ERROR: Cannot resolve key       │
│    IUnknownService                 │
└────────────────────────────────────┘


CREATING CHILD CONTAINERS
════════════════════════════════════════════════════════

const root = DI.createContainer();
root.register(
  Registration.singleton(ILogger, ConsoleLogger),
  Registration.instance(IConfig, appConfig)
);

const feature = root.createChild();
feature.register(
  // Override logger for this feature
  Registration.singleton(ILogger, FeatureLogger),
  // Add feature-specific service
  Registration.singleton(IFeatureService, FeatureService)
);

// Resolution:
root.get(ILogger);     // → ConsoleLogger
feature.get(ILogger);  // → FeatureLogger (override)
feature.get(IConfig);  // → appConfig (inherited from root)


USE CASES FOR CHILD CONTAINERS
════════════════════════════════════════════════════════

1. Feature Modules
   ├─ Override services for specific features
   └─ Isolate feature-specific dependencies

2. Testing
   ├─ Create test container with mocks
   └─ Don't pollute root container

3. Multi-Tenancy
   ├─ Each tenant gets own container
   └─ Override config/services per tenant

4. Component Scoping
   ├─ Component creates child container
   └─ Scoped services destroyed with component

Key Points:

  • Children inherit all parent registrations

  • Children can override parent registrations

  • Resolution walks up the chain

  • First match wins

Container hierarchy documentation →


2. Service Lifetimes

How different lifetime registrations behave:

Decision Guide:

  • Singleton: When you need shared state or expensive resources

  • Transient: When each consumer needs independent instances

  • Scoped: When you need per-component or per-feature instances

Service lifetimes documentation →


3. Injection Methods Comparison

Three ways to inject dependencies:

Recommendation: Use resolve() for new Aurelia 2 code. It's cleaner, more flexible, and works better with modern JavaScript/TypeScript.

Injection patterns documentation →


4. Registration Flow

How services get registered in the container:

Recommendation: Use DI.createInterface() with auto-registration for services. Use manual registration for configuration, instances, and special cases.

Registration documentation →


5. Resolver Pipeline

How resolvers modify dependency resolution:

Key Points:

  • Resolvers wrap keys to modify behavior

  • Most resolve without resolvers (simplest/fastest)

  • Use resolvers for special cases only

  • Can chain resolvers (but rarely needed)

Resolvers documentation →


6. Property vs Constructor Injection

Comparing the two injection styles:

Recommendation: Use property injection with resolve() for Aurelia 2 projects. It's simpler, cleaner, and works better with modern JavaScript patterns.

Property injection documentation →


7. Interface Token Creation

How DI.createInterface() works:

Key Takeaways:

  • DI.createInterface() creates a Symbol token + auto-registration

  • Type alias copies the shape of the implementation class

  • Auto-registration is lazy (happens on first request)

  • Friendly names improve debugging experience

Creating services documentation →


Summary

These diagrams cover the core architectural concepts of Aurelia 2's dependency injection:

  1. Container Hierarchy - How containers inherit and override registrations

  2. Service Lifetimes - Singleton vs Transient vs Scoped behavior

  3. Injection Methods - resolve() vs @inject vs static inject

  4. Registration Flow - Auto-registration vs manual registration

  5. Resolver Pipeline - How resolvers modify dependency resolution

  6. Property vs Constructor - Comparing injection styles

  7. Interface Tokens - How DI.createInterface() works

For more details, see the complete DI Documentation.

Last updated

Was this helpful?