Context System and Dependency Injection
Last Updated: May 11, 2025
The Context System in Typus Development Framework provides a powerful mechanism for dependency injection and request-scoped data management. This document explains how the context system works and how it enables clean, decoupled code through dependency injection.
Overview
Core Components
Context Class
The Context
class is the foundation of the context system. It provides a container for request-scoped data and dependencies:
export interface IContext {
readonly id: string;
readonly startTime: Date;
set(key: string, value: any): void;
get<T>(key: string): T | undefined;
has(key: string): boolean;
getDuration(): number;
getSnapshot(): Record<string, any>;
}
Context Manager
The ContextManager
is a singleton that manages context instances using Node.js AsyncLocalStorage
:
export interface IContextManager {
run<T>(callback: () => T): T;
runAsync<T>(callback: () => Promise<T>): Promise<T>;
getCurrentContext(): IContext | undefined;
middleware(): (req: any, res: any, next: any) => void;
}
Dependency Injection Container
The context system is extended with a dependency injection container that manages service instances:
export interface IContainer {
register(name: string, service: any): void;
get<T>(name: string): T;
}
How It Works
1. Context Creation
For each HTTP request, the context middleware creates a new context:
app.use(ContextManager.getInstance().middleware());
This ensures that each request has its own isolated context.
2. Service Registration
During application startup, services are registered with the container:
const container = Container.getInstance();
container.register('UserService', new UserService());
container.register('AuthService', new AuthService());
container.register('EmailService', new EmailService());
3. Dependency Injection
Components can access services through the context:
const contextManager = ContextManager.getInstance();
const context = contextManager.getCurrentContext();
if (context) {
const userService = context.get<UserService>('UserService');
const user = await userService.getCurrentUser();
}
Request Flow with Context
Context in Frontend
The frontend also has a context system, often implemented using Vue's provide
/inject
or a similar pattern, to manage dependencies and shared state within the client-side application.
This allows components to access shared services without prop drilling.
Benefits of the Context System
1. Request Isolation
Each request has its own context, ensuring data isolation between concurrent requests.
2. Dependency Injection
Services and components can be injected without tight coupling:
// Without context/DI
constructor(
private userService: UserService,
private authService: AuthService,
private emailService: EmailService
) {}
// With context/DI
const context = ContextManager.getInstance().getCurrentContext();
const userService = context.get<UserService>('UserService');
3. Request-Scoped Data
The context can store request-specific data that is accessible throughout the request lifecycle:
// In authentication middleware
context.set('user', authenticatedUser);
// Later in a service
const user = context.get('user');
4. Simplified Testing
Components can be tested with mock dependencies:
// In a test
const mockContext = new Context();
mockContext.set('UserService', mockUserService);
ContextManager.getInstance().run(() => {
// Test code that uses UserService
});
5. Cross-Cutting Concerns
The context system facilitates implementation of cross-cutting concerns like logging, performance monitoring, and tracing:
// Add request ID for tracing
context.set('requestId', uuidv4());
// Log with context information
logger.info('Processing request', {
requestId: context.get('requestId'),
user: context.get('user')?.id,
path: context.get('path')
});
Best Practices
- Service Registration: Register services during application startup
- Context Access: Always check if context exists before accessing it
- Scoped Data: Use context for request-scoped data, not for global state
- Performance: Be mindful of storing large objects in context
- Type Safety: Use generics when retrieving values from context
- Testing: Create mock contexts for unit testing
- Documentation: Document what services and data are available in context