Error Handling Patterns
Error Handling Patterns
Section titled “Error Handling Patterns”This document outlines the error handling strategies, patterns, and domain-specific error types used throughout the Bartendie system.
Error Handling Philosophy
Section titled “Error Handling Philosophy”The Bartendie system follows these principles for error handling:
- Fail Fast: Detect and report errors as early as possible
- Explicit Error Types: Use domain-specific error types rather than generic exceptions
- Graceful Degradation: Continue operating with reduced functionality when possible
- User-Friendly Messages: Provide clear, actionable error messages to users
- Comprehensive Logging: Log all errors with sufficient context for debugging
Domain Error Types
Section titled “Domain Error Types”Event Planning Errors
Section titled “Event Planning Errors”classDiagram
class EventError {
<<Abstract>>
+ErrorCode String
+Message String
+Timestamp DateTime
+GetUserMessage() String
}
class EventDateError {
+EventDate DateTime
+MinimumDate DateTime
}
class VenueCapacityError {
+RequestedCapacity Integer
+MaxCapacity Integer
+VenueId UUID
}
class EventConflictError {
+ConflictingEventId UUID
+ConflictingDate DateTime
+VenueId UUID
}
class GuestListError {
+DuplicateGuestId UUID
+InvalidRSVPTransition String
}
EventError <|-- EventDateError
EventError <|-- VenueCapacityError
EventError <|-- EventConflictError
EventError <|-- GuestListError
Recipe and Menu Errors
Section titled “Recipe and Menu Errors”classDiagram
class RecipeError {
<<Abstract>>
+RecipeId UUID
+ErrorCode String
+Message String
}
class IngredientNotFoundError {
+IngredientName String
+SuggestedAlternatives List~String~
}
class RecipeBalanceError {
+CurrentBalance RecipeBalance
+RecommendedAdjustments List~String~
}
class MenuValidationError {
+InvalidItems List~DrinkItem~
+MissingIngredients List~String~
}
class AlcoholContentError {
+CurrentPercentage Float
+MaxAllowedPercentage Float
}
RecipeError <|-- IngredientNotFoundError
RecipeError <|-- RecipeBalanceError
RecipeError <|-- MenuValidationError
RecipeError <|-- AlcoholContentError
Inventory and Shopping Errors
Section titled “Inventory and Shopping Errors”classDiagram
class InventoryError {
<<Abstract>>
+ProductId UUID
+ErrorCode String
+Message String
}
class InsufficientStockError {
+RequiredQuantity Decimal
+AvailableQuantity Decimal
+Unit MeasurementUnit
}
class ProductExpiredError {
+ExpirationDate DateTime
+DaysExpired Integer
}
class BarcodeError {
+Barcode String
+ErrorType BarcodeErrorType
}
class SupplierError {
+SupplierId UUID
+SupplierName String
+ErrorType SupplierErrorType
}
InventoryError <|-- InsufficientStockError
InventoryError <|-- ProductExpiredError
InventoryError <|-- BarcodeError
InventoryError <|-- SupplierError
User and Security Errors
Section titled “User and Security Errors”classDiagram
class SecurityError {
<<Abstract>>
+UserId UUID
+Resource String
+Action String
+ErrorCode String
}
class AuthenticationError {
+Username String
+FailureReason String
+AttemptCount Integer
}
class AuthorizationError {
+RequiredPermission String
+UserRoles List~String~
}
class ValidationError {
+FieldName String
+FieldValue String
+ValidationRule String
}
SecurityError <|-- AuthenticationError
SecurityError <|-- AuthorizationError
SecurityError <|-- ValidationError
Error Handling Patterns
Section titled “Error Handling Patterns”1. Result Pattern
Section titled “1. Result Pattern”Use the Result pattern for operations that can fail:
// Example Result pattern implementationclass Result<T, E> { constructor( private readonly value?: T, private readonly error?: E, private readonly isSuccess: boolean = true ) {}
static success<T>(value: T): Result<T, never> { return new Result(value, undefined, true); }
static failure<E>(error: E): Result<never, E> { return new Result(undefined, error, false); }
isOk(): boolean { return this.isSuccess; }
isErr(): boolean { return !this.isSuccess; }
getValue(): T { if (!this.isSuccess) { throw new Error("Cannot get value from failed result"); } return this.value!; }
getError(): E { if (this.isSuccess) { throw new Error("Cannot get error from successful result"); } return this.error!; }}2. Command Validation Pattern
Section titled “2. Command Validation Pattern”Validate commands before processing:
class CreateEventCommand { validate(): Result<void, ValidationError[]> { const errors: ValidationError[] = [];
if (this.date <= new Date()) { errors.push(new ValidationError("date", this.date.toString(), "must_be_future")); }
if (this.guestCount <= 0) { errors.push(new ValidationError("guestCount", this.guestCount.toString(), "must_be_positive")); }
return errors.length > 0 ? Result.failure(errors) : Result.success(undefined); }}3. Aggregate Invariant Protection
Section titled “3. Aggregate Invariant Protection”Protect aggregate invariants with domain exceptions:
class Event { addGuest(guest: Guest): Result<void, EventError> { if (this.guests.length >= this.venue.maxCapacity) { return Result.failure(new VenueCapacityError( this.guests.length + 1, this.venue.maxCapacity, this.venue.id )); }
if (this.guests.some(g => g.id === guest.id)) { return Result.failure(new GuestListError(guest.id, "duplicate_guest")); }
this.guests.push(guest); return Result.success(undefined); }}4. External System Error Handling
Section titled “4. External System Error Handling”Handle external system failures gracefully:
class ProductLookupService { async lookupProduct(barcode: string): Promise<Result<ProductData, InventoryError>> { try { const response = await this.externalProductAPI.lookup(barcode); return Result.success(this.mapToProductData(response)); } catch (error) { if (error instanceof NetworkError) { // Fallback to cached data or manual entry return this.handleNetworkFailure(barcode); }
if (error instanceof NotFoundError) { return Result.failure(new BarcodeError(barcode, "product_not_found")); }
// Log unexpected errors this.logger.error("Unexpected error in product lookup", { barcode, error }); return Result.failure(new BarcodeError(barcode, "lookup_failed")); } }
private async handleNetworkFailure(barcode: string): Promise<Result<ProductData, InventoryError>> { const cachedData = await this.cache.get(barcode); if (cachedData) { this.notificationService.warn("Using cached product data due to network issues"); return Result.success(cachedData); }
return Result.failure(new BarcodeError(barcode, "network_unavailable")); }}Error Recovery Strategies
Section titled “Error Recovery Strategies”1. Compensation Actions
Section titled “1. Compensation Actions”For failed operations that require cleanup:
class MenuCreationService { async createMenu(command: CreateMenuCommand): Promise<Result<Menu, MenuError>> { const compensationActions: (() => Promise<void>)[] = [];
try { // Step 1: Create menu const menu = await this.menuRepository.create(command.toMenu()); compensationActions.push(() => this.menuRepository.delete(menu.id));
// Step 2: Reserve ingredients const reservationResult = await this.inventoryService.reserveIngredients(command.ingredients); if (reservationResult.isErr()) { await this.executeCompensation(compensationActions); return Result.failure(reservationResult.getError()); } compensationActions.push(() => this.inventoryService.releaseReservation(reservationResult.getValue()));
// Step 3: Validate menu balance const balanceResult = this.validateMenuBalance(menu); if (balanceResult.isErr()) { await this.executeCompensation(compensationActions); return Result.failure(balanceResult.getError()); }
return Result.success(menu); } catch (error) { await this.executeCompensation(compensationActions); throw error; } }
private async executeCompensation(actions: (() => Promise<void>)[]): Promise<void> { for (const action of actions.reverse()) { try { await action(); } catch (compensationError) { this.logger.error("Compensation action failed", { compensationError }); } } }}2. Circuit Breaker Pattern
Section titled “2. Circuit Breaker Pattern”Prevent cascading failures from external systems:
class CircuitBreaker { private failureCount = 0; private lastFailureTime?: Date; private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
constructor( private readonly failureThreshold: number = 5, private readonly timeoutMs: number = 60000 ) {}
async execute<T>(operation: () => Promise<T>): Promise<Result<T, Error>> { if (this.state === 'OPEN') { if (this.shouldAttemptReset()) { this.state = 'HALF_OPEN'; } else { return Result.failure(new Error("Circuit breaker is OPEN")); } }
try { const result = await operation(); this.onSuccess(); return Result.success(result); } catch (error) { this.onFailure(); return Result.failure(error); } }
private onSuccess(): void { this.failureCount = 0; this.state = 'CLOSED'; }
private onFailure(): void { this.failureCount++; this.lastFailureTime = new Date();
if (this.failureCount >= this.failureThreshold) { this.state = 'OPEN'; } }
private shouldAttemptReset(): boolean { return this.lastFailureTime && (Date.now() - this.lastFailureTime.getTime()) > this.timeoutMs; }}Error Monitoring and Alerting
Section titled “Error Monitoring and Alerting”Error Severity Levels
Section titled “Error Severity Levels”- CRITICAL: System-wide failures, data corruption, security breaches
- HIGH: Feature unavailability, external system failures
- MEDIUM: Validation errors, business rule violations
- LOW: User input errors, expected failures
Monitoring Patterns
Section titled “Monitoring Patterns”class ErrorMonitor { logError(error: Error, context: ErrorContext): void { const errorData = { id: generateId(), timestamp: new Date(), severity: this.determineSeverity(error), type: error.constructor.name, message: error.message, stackTrace: error.stack, context: context, userId: context.userId, correlationId: context.correlationId };
// Log to structured logging system this.logger.error(errorData);
// Send to monitoring system this.metricsCollector.incrementCounter('errors_total', { type: error.constructor.name, severity: errorData.severity });
// Trigger alerts for critical errors if (errorData.severity === 'CRITICAL') { this.alertingService.sendAlert(errorData); } }
private determineSeverity(error: Error): string { if (error instanceof SecurityError) return 'CRITICAL'; if (error instanceof InventoryError) return 'HIGH'; if (error instanceof ValidationError) return 'LOW'; return 'MEDIUM'; }}This comprehensive error handling approach ensures the Bartendie system can gracefully handle failures while providing clear feedback to users and maintaining system stability.