# How the ServiceContainer Utilizes Dependency Injection to Manage Extension Services in Secure Design

> Learn how the ServiceContainer uses dependency injection to manage extension services. Discover its deterministic instantiation and type-safe retrieval methods for efficient service management.

- Repository: [Harold Martin/secure-design](https://github.com/hbmartin/secure-design)
- Tags: internals
- Published: 2026-03-03

---

**The ServiceContainer in the Secure Design extension implements dependency injection by instantiating services in a deterministic order, storing them in an internal Map, and exposing a generic `get<T>()` method for type-safe retrieval across the codebase.**

The Secure Design VS Code extension employs a lightweight ServiceContainer to manage its dependency injection architecture. This centralized approach ensures that extension services are constructed once, shared efficiently, and properly disposed of when the extension deactivates.

## Service Container Architecture and Core Implementation

The dependency injection mechanism is implemented in [`src/di/ServiceContainer.ts`](https://github.com/hbmartin/secure-design/blob/main/src/di/ServiceContainer.ts). This class maintains an internal `Map<string, any>` named `services` that stores instantiated services against logical string keys.

The container exposes four primary methods:

- **`initialize()`**: Constructs all services in dependency order
- **`get<T>(name: string): T`**: Retrieves typed service instances
- **`has(name: string): boolean`**: Checks service existence
- **`dispose()`**: Cleans up resources on extension shutdown

## Service Registration and Initialization Process

During extension activation, the `initialize()` method creates concrete instances of all required services. The implementation in [`src/di/ServiceContainer.ts`](https://github.com/hbmartin/secure-design/blob/main/src/di/ServiceContainer.ts) instantiates the following services in deterministic order:

1. **`WorkspaceStateService`** - Manages persistent state
2. **`CustomAgentService`** - Handles AI provider integration
3. **`ChatMessagesRepository`** - Stores chat message history
4. **`WebviewApiProvider`** - Provides webview communication
5. **`ChatController`** - Orchestrates chat functionality
6. **`ChatSidebarProvider`** - Manages sidebar UI components

Each service is registered using `this.services.set(name, instance)`, making them available for retrieval throughout the extension lifecycle.

## Dependency Resolution and Type-Safe Retrieval

The `get<T>()` method enables loose coupling by allowing any component to request dependencies by name rather than importing concrete classes. This generic method returns properly typed instances, providing compile-time safety.

In [`src/extension.ts`](https://github.com/hbmartin/secure-design/blob/main/src/extension.ts) (lines 34-45), the activation sequence demonstrates practical usage:

```typescript
export function activate(context: vscode.ExtensionContext): void {
    const serviceContainer = new ServiceContainer(context);
    serviceContainer.initialize();
    
    const sidebar = serviceContainer.get<ChatSidebarProvider>('sidebarProvider');
    const apiProvider = serviceContainer.get<WebviewApiProvider>('apiProvider');
    
    context.subscriptions.push(serviceContainer);
}

```

Commands and UI components throughout the extension use similar patterns to access shared services without direct instantiation.

## Lifecycle Management and Resource Disposal

The ServiceContainer implements `vscode.Disposable` to ensure proper cleanup when the extension deactivates. The `dispose()` method iterates through the internal services Map and invokes `dispose()` on any service implementing the interface.

Implementation in [`src/di/ServiceContainer.ts`](https://github.com/hbmartin/secure-design/blob/main/src/di/ServiceContainer.ts):

```typescript
class ServiceContainer implements vscode.Disposable {
    dispose(): void {
        for (const [name, svc] of this.services) {
            if (svc?.dispose) {
                svc.dispose();
            }
        }
        this.services.clear();
    }
}

```

This pattern guarantees that file watchers, webview panels, and background tasks release resources properly, preventing memory leaks during extension reloads or VS Code shutdown.

## Summary

- The **ServiceContainer** in [`src/di/ServiceContainer.ts`](https://github.com/hbmartin/secure-design/blob/main/src/di/ServiceContainer.ts) provides a centralized dependency injection mechanism using an internal Map to store service instances.
- Services are instantiated in **deterministic order** during `initialize()`, ensuring dependencies are available before dependent services are created.
- The **generic `get<T>()` method** enables type-safe, loose coupling throughout the extension, allowing components to request dependencies by logical name.
- **Lifecycle management** is handled through the `dispose()` method, which iterates registered services and invokes their disposal methods to prevent resource leaks.
- Extension activation in [`src/extension.ts`](https://github.com/hbmartin/secure-design/blob/main/src/extension.ts) demonstrates the practical pattern: create container, initialize services, retrieve specific services via `get()`, and register container for automatic disposal.

## Frequently Asked Questions

### What is the purpose of the ServiceContainer in the Secure Design extension?

The ServiceContainer implements a lightweight dependency injection pattern that centralizes service creation and management. It eliminates tight coupling between components by allowing services to request dependencies through a generic retrieval interface rather than instantiating them directly, making the codebase more testable and maintainable.

### How does the ServiceContainer handle service disposal when the extension deactivates?

The ServiceContainer implements the `vscode.Disposable` interface and registers itself with the extension context's subscriptions. When VS Code deactivates the extension, it automatically calls the container's `dispose()` method, which iterates through the internal services Map and invokes `dispose()` on any service that implements the disposable pattern, ensuring proper cleanup of resources.

### Why does the ServiceContainer use a Map instead of a plain object for service storage?

The ServiceContainer uses a `Map<string, any>` because it provides better type safety for keys, preserves insertion order during iteration (important for deterministic disposal), and offers optimized performance characteristics for frequent get/set operations. Additionally, Maps avoid prototype pollution issues that can occur with plain JavaScript objects when using dynamic string keys.

### Can services retrieve other services from the ServiceContainer after initialization?

Yes, any part of the extension can retrieve services after initialization by calling `serviceContainer.get<ServiceType>('serviceName')`. This pattern is used throughout the codebase, including in [`src/extension.ts`](https://github.com/hbmartin/secure-design/blob/main/src/extension.ts) where the activation function retrieves the `ChatSidebarProvider` and `WebviewApiProvider` after initialization to register them with VS Code's UI subsystem.