LLM Middleware follows Clean Architecture principles with clear separation of concerns and dependency inversion.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Application Layer β
β βββββββββββββββββββ βββββββββββββββββββ ββββββββββββββββ β
β β Controllers β β Use Cases β β Examples β β
β β (HTTP Layer) β β (Business Logic)β β (Apps) β β
β βββββββββββββββββββ βββββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Infrastructure Layer β
β βββββββββββββββββββ βββββββββββββββββββ ββββββββββββββββ β
β β Services β β Configuration β β Logging β β
β β(External APIs) β β Management β β & Utils β β
β βββββββββββββββββββ βββββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
src/
βββ middleware/ # Core middleware components
β βββ controllers/ # HTTP request handling
β β βββ base/ # Base controller classes
β βββ usecases/ # Business logic layer
β β βββ base/ # Base use case classes
β βββ services/ # External service integrations
β β βββ llm/ # LLM provider services (Ollama, OpenAI, etc.)
β β βββ json-cleaner/ # JSON processing & repair
β β βββ response-processor/ # AI response processing
β β βββ data-flow-logger/ # Request/response logging
β β βββ model-parameter-manager/ # Model configuration
β β βββ use-case-metrics-logger/ # Performance metrics
β βββ shared/ # Common utilities
β β βββ config/ # Configuration management
β β βββ types/ # TypeScript interfaces
β β βββ utils/ # Utility functions
β β βββ middleware/ # Express middleware
β βββ logging/ # Log file management
βββ examples/ # Example implementations
β βββ simple-chat/ # Basic chat example
β βββ test-integration/ # Integration tests
βββ templates/ # Code templates for new projects
Responsibilities:
- Handle HTTP requests and responses
- Request validation and parsing
- Response formatting
- Error handling and status codes
Key Components:
BaseController- Common request handling logic- Error handling middleware
- Client info extraction
Example:
class ChatController extends BaseController {
public async chat(req: RequestWithUser, res: Response): Promise<void> {
await this.handleRequest(req, res, async () => {
// Delegate to use case
const result = await this.chatUseCase.execute(req.body);
return result;
});
}
}Responsibilities:
- Orchestrate business operations
- Coordinate between services
- Enforce business rules
- Handle application-specific logic
Key Components:
BaseAIUseCase- Template for AI-powered use cases- Request/response formatting
- Service coordination
Example:
class ChatUseCase extends BaseAIUseCase<ChatRequest, ChatResult> {
protected readonly systemMessage = "You are a helpful assistant";
protected createResult(content: string, prompt: string): ChatResult {
return {
generatedContent: content,
model: this.modelConfig.name,
usedPrompt: prompt,
response: content
};
}
}Responsibilities:
- External API communications
- Data transformation and cleaning
- Caching and performance optimization
- Error recovery and retries
Key Components:
LLMService- Multi-provider LLM integration (Ollama, OpenAI, Anthropic, Google)JsonCleanerService- AI response processingDataFlowLoggerService- Request/response loggingModelParameterManager- Model configuration
Responsibilities:
- Configuration management
- Logging utilities
- Type definitions
- Common helper functions
Key Components:
- Configuration system
- Logger with multiple levels
- TypeScript interfaces
- HTTP utilities
The BaseAIUseCase uses the template method pattern:
abstract class BaseAIUseCase<TRequest, TResult> {
// Template method
public async execute(request: TRequest): Promise<TResult> {
const formatted = this.formatUserMessage(request.prompt);
const response = await this.callLLMProvider(formatted);
const processed = this.processResponse(response);
return this.createResult(processed, formatted);
}
// Abstract methods - must be implemented by subclasses
protected abstract createResult(content: string, prompt: string): TResult;
// Hook methods - can be overridden
protected formatUserMessage(prompt: any): string { ... }
protected processResponse(response: string): string { ... }
}JSON cleaning uses the strategy pattern:
class JsonCleanerOrchestrator {
private strategies: CleaningStrategy[] = [];
addStrategy(strategy: CleaningStrategy) {
this.strategies.push(strategy);
}
clean(json: string): string {
for (const strategy of this.strategies) {
if (strategy.canHandle(json)) {
return strategy.clean(json);
}
}
throw new Error('No strategy could handle the JSON');
}
}Model configuration uses the factory pattern:
export function getModelConfig(key: ModelConfigKey): LLMModelConfig {
return MODELS[key];
}Services are injected into use cases:
class MyUseCase extends BaseAIUseCase<MyRequest, MyResult> {
constructor(
private customService?: CustomService
) {
super();
}
}HTTP Request β Controller β Use Case β Services β External APIs
β β β β
Validation β Business β Processing β LLM Provider APIs
β Logic β β
Client Info β Logging β JSON Clean β Response
β β β β
HTTP Response β Result β Formatted β Processed
Environment Variables β App Config β Model Config β Use Case Config
β β β β
.env file β appConfig object β getModelConfig() β this.modelConfig
Application Events β Logger β Console/Database β Log Files
β β β β
log.info() β formatMessage() β writeLog() β /logs/*.log
Service Error β Use Case Error β Controller Error β HTTP Response
β β β β
Technical β Business Logic β User-Friendly β JSON Response
- Technical Errors: Network failures, parsing errors
- Business Errors: Validation failures, rule violations
- User Errors: Invalid input, missing parameters
- Retry mechanisms in services
- Fallback strategies for AI responses
- Graceful degradation
- Model configuration caching
- Response caching for identical requests
- JSON cleaning pattern caching
- Connection pooling for HTTP clients
- Memory management for large responses
- Timeout handling for long operations
- Request/response logging
- Performance metrics collection
- Error rate tracking
- Extend
BaseAIUseCase - Implement required abstract methods
- Add controller endpoints
- Register routes
- Create service interface
- Implement service class
- Add configuration options
- Integrate with dependency injection
- Implement
CleaningStrategyinterface - Register with
JsonCleanerOrchestrator - Add configuration options
- Test with various JSON patterns
- Individual service testing
- Use case logic validation
- Configuration testing
- End-to-end request flows
- Service integration validation
- Error handling verification
- Load testing for concurrent requests
- Memory usage monitoring
- Response time validation
- Request parameter validation
- JSON schema validation
- SQL injection prevention
- Token-based authentication
- API key management
- Rate limiting
- Sensitive data filtering in logs
- Secure configuration management
- Input sanitization
This architecture provides a solid foundation for building scalable, maintainable AI-powered applications while following established software engineering principles.