Chapter 14 System Architecture and Design Patterns - Bryantad/Sona GitHub Wiki

Chapter 14: System Architecture and Design Patterns

Building Large-Scale, Maintainable Applications: Architecture Patterns for the Real World


Andre's Architecture Philosophy

"Great software architecture isn't about using the most complex patterns or the latest frameworksβ€”it's about solving real problems with clarity and purpose. Over the years building Sona and various applications, I've learned that the best architectures are those that grow naturally with your needs, remain understandable to your team, and can adapt to changing requirements. This chapter will teach you to think like a software architect."

The Architecture Evolution Path

System Complexity vs Architecture Needs
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Simple Script                                  β”‚
β”‚              ↓                                              β”‚
β”‚              Single Application                             β”‚
β”‚              ↓                                              β”‚
β”‚              Modular Application                            β”‚
β”‚              ↓                                              β”‚
β”‚              Distributed System                             β”‚
β”‚              ↓                                              β”‚
β”‚              Microservices Architecture                     β”‚
β”‚              ↓                                              β”‚
β”‚              Event-Driven Architecture                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Learning Objectives

By the end of this chapter, you will:

  • Design scalable application architectures using proven patterns
  • Implement Domain-Driven Design (DDD) principles in Sona
  • Build event-driven systems with reliable messaging
  • Create clean, maintainable code using SOLID principles
  • Design APIs that can evolve without breaking clients
  • Implement monitoring, logging, and observability patterns
  • Handle system failures gracefully with resilience patterns
  • Build security into your architecture from the ground up

Section 1: Layered Architecture and Domain-Driven Design

Building a Clean Architecture Foundation

// Clean Architecture implementation in Sona
// Domain Layer - Core business logic, no external dependencies
class User {
    id: string
    email: string
    username: string
    created_at: datetime
    is_active: bool

    constructor(id, email, username) {
        self.id = id
        self.email = email
        self.username = username
        self.created_at = datetime.now()
        self.is_active = true

        self.validate()
    }

    validate() {
        if not self.email or not self.email.contains("@") {
            throw InvalidEmailError("Email must be valid")
        }

        if not self.username or self.username.length < 3 {
            throw InvalidUsernameError("Username must be at least 3 characters")
        }
    }

    activate() {
        self.is_active = true
        return UserActivatedEvent(self.id, datetime.now())
    }

    deactivate() {
        self.is_active = false
        return UserDeactivatedEvent(self.id, datetime.now())
    }

    change_email(new_email) {
        let old_email = self.email
        self.email = new_email
        self.validate()

        return EmailChangedEvent(self.id, old_email, new_email, datetime.now())
    }
}

// Domain Services - Complex business logic that doesn't belong to entities
class UserDomainService {
    user_repository: UserRepository

    constructor(user_repository) {
        self.user_repository = user_repository
    }

    async is_username_unique(username, excluding_user_id = null) {
        let existing_user = await self.user_repository.find_by_username(username)

        if not existing_user {
            return true
        }

        return excluding_user_id and existing_user.id == excluding_user_id
    }

    async calculate_user_metrics(user_id) {
        let user = await self.user_repository.find_by_id(user_id)
        if not user {
            throw UserNotFoundError(f"User {user_id} not found")
        }

        // Complex domain logic for calculating user metrics
        let account_age_days = (datetime.now() - user.created_at).days
        let activity_score = await self.calculate_activity_score(user)
        let engagement_level = self.classify_engagement_level(activity_score, account_age_days)

        return UserMetrics(
            user_id: user.id,
            account_age_days: account_age_days,
            activity_score: activity_score,
            engagement_level: engagement_level
        )
    }

    async calculate_activity_score(user) {
        // Domain-specific calculation logic
        // This would integrate with other domain services
        return 85.5  // Simplified for example
    }

    classify_engagement_level(activity_score, account_age_days) {
        if activity_score > 80 and account_age_days > 30 {
            return "HIGHLY_ENGAGED"
        } elif activity_score > 50 {
            return "MODERATELY_ENGAGED"
        } else {
            return "LOW_ENGAGEMENT"
        }
    }
}

// Application Layer - Use cases and application services
class CreateUserUseCase {
    user_repository: UserRepository
    user_domain_service: UserDomainService
    event_dispatcher: EventDispatcher

    constructor(user_repository, user_domain_service, event_dispatcher) {
        self.user_repository = user_repository
        self.user_domain_service = user_domain_service
        self.event_dispatcher = event_dispatcher
    }

    async execute(command) {
        // Validate command
        self.validate_command(command)

        // Check business rules
        let is_username_unique = await self.user_domain_service.is_username_unique(command.username)
        if not is_username_unique {
            throw UsernameAlreadyExistsError(f"Username '{command.username}' is already taken")
        }

        // Create domain entity
        let user = User(
            id: generate_user_id(),
            email: command.email,
            username: command.username
        )

        // Persist entity
        await self.user_repository.save(user)

        // Dispatch domain events
        let user_created_event = UserCreatedEvent(
            user_id: user.id,
            email: user.email,
            username: user.username,
            occurred_at: datetime.now()
        )

        await self.event_dispatcher.dispatch(user_created_event)

        return CreateUserResult(
            user_id: user.id,
            success: true,
            message: "User created successfully"
        )
    }

    validate_command(command) {
        if not command.email {
            throw ValidationError("Email is required")
        }

        if not command.username {
            throw ValidationError("Username is required")
        }

        if command.password and command.password.length < 8 {
            throw ValidationError("Password must be at least 8 characters")
        }
    }
}

// Infrastructure Layer - External concerns (databases, APIs, etc.)
class PostgreSQLUserRepository implements UserRepository {
    database: Database

    constructor(database) {
        self.database = database
    }

    async save(user) {
        let query = """
            INSERT INTO users (id, email, username, created_at, is_active)
            VALUES ($1, $2, $3, $4, $5)
            ON CONFLICT (id) DO UPDATE SET
                email = EXCLUDED.email,
                username = EXCLUDED.username,
                is_active = EXCLUDED.is_active
        """

        await self.database.execute(query, [
            user.id,
            user.email,
            user.username,
            user.created_at,
            user.is_active
        ])
    }

    async find_by_id(user_id) {
        let query = "SELECT * FROM users WHERE id = $1"
        let result = await self.database.query_one(query, [user_id])

        if not result {
            return null
        }

        return self.map_to_domain_entity(result)
    }

    async find_by_username(username) {
        let query = "SELECT * FROM users WHERE username = $1"
        let result = await self.database.query_one(query, [username])

        if not result {
            return null
        }

        return self.map_to_domain_entity(result)
    }

    map_to_domain_entity(row) {
        let user = User(row.id, row.email, row.username)
        user.created_at = row.created_at
        user.is_active = row.is_active
        return user
    }
}

// Presentation Layer - Controllers and API endpoints
class UserController {
    create_user_use_case: CreateUserUseCase
    get_user_use_case: GetUserUseCase

    constructor(create_user_use_case, get_user_use_case) {
        self.create_user_use_case = create_user_use_case
        self.get_user_use_case = get_user_use_case
    }

    async create_user(request) {
        try {
            let command = CreateUserCommand(
                email: request.body.email,
                username: request.body.username,
                password: request.body.password
            )

            let result = await self.create_user_use_case.execute(command)

            return {
                "status": 201,
                "body": {
                    "success": true,
                    "user_id": result.user_id,
                    "message": result.message
                }
            }

        } catch (ValidationError as e) {
            return {
                "status": 400,
                "body": {
                    "success": false,
                    "error": "VALIDATION_ERROR",
                    "message": e.message
                }
            }
        } catch (UsernameAlreadyExistsError as e) {
            return {
                "status": 409,
                "body": {
                    "success": false,
                    "error": "USERNAME_EXISTS",
                    "message": e.message
                }
            }
        } catch (error) {
            log_error("user_creation_failed", {
                "error": error.message,
                "request_id": request.id
            })

            return {
                "status": 500,
                "body": {
                    "success": false,
                    "error": "INTERNAL_ERROR",
                    "message": "An unexpected error occurred"
                }
            }
        }
    }

    async get_user(request) {
        try {
            let user_id = request.params.user_id
            let query = GetUserQuery(user_id: user_id)

            let result = await self.get_user_use_case.execute(query)

            return {
                "status": 200,
                "body": {
                    "user": {
                        "id": result.user.id,
                        "email": result.user.email,
                        "username": result.user.username,
                        "created_at": result.user.created_at.iso_string(),
                        "is_active": result.user.is_active
                    }
                }
            }

        } catch (UserNotFoundError) {
            return {
                "status": 404,
                "body": {
                    "success": false,
                    "error": "USER_NOT_FOUND",
                    "message": "User not found"
                }
            }
        }
    }
}

Section 2: Event-Driven Architecture

Building Robust Event Systems

// Event-driven architecture implementation
abstract class DomainEvent {
    event_id: string
    occurred_at: datetime
    version: int

    constructor() {
        self.event_id = generate_event_id()
        self.occurred_at = datetime.now()
        self.version = 1
    }

    abstract get_event_type(): string
    abstract to_dict(): {}
}

class UserCreatedEvent extends DomainEvent {
    user_id: string
    email: string
    username: string

    constructor(user_id, email, username) {
        super()
        self.user_id = user_id
        self.email = email
        self.username = username
    }

    get_event_type() {
        return "USER_CREATED"
    }

    to_dict() {
        return {
            "event_id": self.event_id,
            "event_type": self.get_event_type(),
            "occurred_at": self.occurred_at.iso_string(),
            "version": self.version,
            "data": {
                "user_id": self.user_id,
                "email": self.email,
                "username": self.username
            }
        }
    }
}

// Event Store for persistence and replay
class EventStore {
    database: Database

    constructor(database) {
        self.database = database
    }

    async save_event(event) {
        let query = """
            INSERT INTO events (event_id, event_type, aggregate_id, data, occurred_at, version)
            VALUES ($1, $2, $3, $4, $5, $6)
        """

        let aggregate_id = self.extract_aggregate_id(event)

        await self.database.execute(query, [
            event.event_id,
            event.get_event_type(),
            aggregate_id,
            json.stringify(event.to_dict()),
            event.occurred_at,
            event.version
        ])
    }

    async get_events_for_aggregate(aggregate_id, from_version = 0) {
        let query = """
            SELECT * FROM events
            WHERE aggregate_id = $1 AND version > $2
            ORDER BY version ASC
        """

        let rows = await self.database.query(query, [aggregate_id, from_version])

        return rows.map(row => self.deserialize_event(row))
    }

    async get_all_events(from_timestamp = null, limit = 1000) {
        let query = """
            SELECT * FROM events
            WHERE ($1 IS NULL OR occurred_at > $1)
            ORDER BY occurred_at ASC
            LIMIT $2
        """

        let rows = await self.database.query(query, [from_timestamp, limit])

        return rows.map(row => self.deserialize_event(row))
    }

    extract_aggregate_id(event) {
        // Extract the main entity ID from the event
        if hasattr(event, "user_id") {
            return event.user_id
        } elif hasattr(event, "order_id") {
            return event.order_id
        } else {
            return event.event_id  // Fallback
        }
    }

    deserialize_event(row) {
        let event_data = json.parse(row.data)

        // Factory pattern to recreate events
        match row.event_type {
            "USER_CREATED" => {
                let event = UserCreatedEvent(
                    event_data.data.user_id,
                    event_data.data.email,
                    event_data.data.username
                )
                event.event_id = event_data.event_id
                event.occurred_at = datetime.parse(event_data.occurred_at)
                return event
            },
            default => {
                throw UnknownEventTypeError(f"Unknown event type: {row.event_type}")
            }
        }
    }
}

// Event Dispatcher with reliable delivery
class EventDispatcher {
    handlers: {}
    event_store: EventStore
    message_queue: MessageQueue
    retry_policy: RetryPolicy

    constructor(event_store, message_queue) {
        self.handlers = {}
        self.event_store = event_store
        self.message_queue = message_queue
        self.retry_policy = ExponentialBackoffRetryPolicy(
            max_retries: 5,
            base_delay: 1.0,
            max_delay: 60.0
        )
    }

    register_handler(event_type, handler) {
        if event_type not in self.handlers {
            self.handlers[event_type] = []
        }

        self.handlers[event_type].append(handler)
        print(f"Registered handler for {event_type}: {handler.name}")
    }

    async dispatch(event) {
        // Save event first for durability
        await self.event_store.save_event(event)

        // Dispatch to local handlers
        await self.dispatch_locally(event)

        // Publish to message queue for remote handlers
        await self.publish_to_queue(event)
    }

    async dispatch_locally(event) {
        let event_type = event.get_event_type()

        if event_type not in self.handlers {
            return  // No handlers registered
        }

        let handlers = self.handlers[event_type]

        # Dispatch to all handlers concurrently
        let handler_tasks = []
        for handler in handlers {
            let task = self.execute_handler_with_retry(handler, event)
            handler_tasks.append(task)
        }

        # Wait for all handlers to complete
        let results = await gather_with_exceptions(handler_tasks)

        # Log any handler failures
        for i, result in enumerate(results) {
            if result.is_error {
                log_error("event_handler_failed", {
                    "event_id": event.event_id,
                    "event_type": event_type,
                    "handler": handlers[i].name,
                    "error": result.error.message
                })
            }
        }
    }

    async execute_handler_with_retry(handler, event) {
        let attempt = 0
        let last_error = null

        while attempt < self.retry_policy.max_retries {
            try {
                await handler.handle(event)
                return  # Success

            } catch (error) {
                last_error = error
                attempt += 1

                if attempt < self.retry_policy.max_retries {
                    let delay = self.retry_policy.calculate_delay(attempt)
                    print(f"Handler {handler.name} failed (attempt {attempt}), retrying in {delay}s")
                    await sleep(delay)
                }
            }
        }

        # All retries exhausted
        throw HandlerRetryExhaustedError(
            f"Handler {handler.name} failed after {attempt} attempts: {last_error.message}"
        )
    }

    async publish_to_queue(event) {
        let message = {
            "event_id": event.event_id,
            "event_type": event.get_event_type(),
            "event_data": event.to_dict(),
            "published_at": datetime.now().iso_string()
        }

        await self.message_queue.publish(
            topic: "domain_events",
            message: message,
            routing_key: event.get_event_type()
        )
    }
}

// Event Handlers
class SendWelcomeEmailHandler {
    email_service: EmailService

    constructor(email_service) {
        self.email_service = email_service
        self.name = "SendWelcomeEmailHandler"
    }

    async handle(event) {
        if not isinstance(event, UserCreatedEvent) {
            return  # Not interested in this event
        }

        print(f"Sending welcome email to {event.email}")

        await self.email_service.send_email(
            to: event.email,
            subject: f"Welcome {event.username}!",
            template: "welcome_email",
            variables: {
                "username": event.username,
                "user_id": event.user_id
            }
        )

        print(f"Welcome email sent successfully to {event.email}")
    }
}

class UserAnalyticsHandler {
    analytics_service: AnalyticsService

    constructor(analytics_service) {
        self.analytics_service = analytics_service
        self.name = "UserAnalyticsHandler"
    }

    async handle(event) {
        if not isinstance(event, UserCreatedEvent) {
            return
        }

        await self.analytics_service.track_event(
            event_name: "user_registered",
            user_id: event.user_id,
            properties: {
                "email_domain": event.email.split("@")[1],
                "username_length": event.username.length,
                "registration_timestamp": event.occurred_at.iso_string()
            }
        )

        print(f"User registration tracked in analytics: {event.user_id}")
    }
}

Section 3: Microservices Architecture Patterns

Service Mesh and Communication Patterns

// Service registry and discovery
class ServiceRegistry {
    services: {}
    health_checker: HealthChecker
    load_balancer: LoadBalancer

    constructor() {
        self.services = {}
        self.health_checker = HealthChecker()
        self.load_balancer = RoundRobinLoadBalancer()
    }

    register_service(service_name, instance) {
        if service_name not in self.services {
            self.services[service_name] = []
        }

        let service_instance = ServiceInstance(
            id: generate_instance_id(),
            name: service_name,
            host: instance.host,
            port: instance.port,
            metadata: instance.metadata or {},
            health_check_url: instance.health_check_url,
            registration_time: datetime.now()
        )

        self.services[service_name].append(service_instance)

        # Start health checking for this instance
        self.health_checker.start_monitoring(service_instance)

        print(f"Registered service instance: {service_name}@{instance.host}:{instance.port}")
    }

    discover_service(service_name) {
        if service_name not in self.services {
            throw ServiceNotFoundError(f"Service '{service_name}' not registered")
        }

        let healthy_instances = self.services[service_name].filter(
            instance => instance.status == "HEALTHY"
        )

        if healthy_instances.length == 0 {
            throw NoHealthyInstancesError(f"No healthy instances for service '{service_name}'")
        }

        return self.load_balancer.select_instance(healthy_instances)
    }

    deregister_service(service_name, instance_id) {
        if service_name in self.services {
            self.services[service_name] = self.services[service_name].filter(
                instance => instance.id != instance_id
            )

            self.health_checker.stop_monitoring(instance_id)
            print(f"Deregistered service instance: {instance_id}")
        }
    }
}

// API Gateway pattern
class APIGateway {
    service_registry: ServiceRegistry
    rate_limiter: RateLimiter
    auth_service: AuthService
    circuit_breakers: {}

    constructor(service_registry, auth_service) {
        self.service_registry = service_registry
        self.auth_service = auth_service
        self.rate_limiter = RateLimiter()
        self.circuit_breakers = {}

        self.setup_routes()
    }

    setup_routes() {
        # Route configuration with service mapping
        self.routes = {
            "/api/users/*": {
                "service": "user-service",
                "strip_prefix": "/api/users",
                "auth_required": true,
                "rate_limit": "100/minute"
            },
            "/api/orders/*": {
                "service": "order-service",
                "strip_prefix": "/api/orders",
                "auth_required": true,
                "rate_limit": "50/minute"
            },
            "/api/public/*": {
                "service": "public-api-service",
                "strip_prefix": "/api/public",
                "auth_required": false,
                "rate_limit": "1000/minute"
            }
        }
    }

    async handle_request(request) {
        try {
            # Route matching
            let route_config = self.match_route(request.path)
            if not route_config {
                return self.create_error_response(404, "Route not found")
            }

            # Authentication
            if route_config.auth_required {
                let auth_result = await self.auth_service.authenticate(request)
                if not auth_result.success {
                    return self.create_error_response(401, "Authentication required")
                }
                request.user = auth_result.user
            }

            # Rate limiting
            let rate_limit_result = await self.rate_limiter.check_limit(
                request.client_ip,
                route_config.rate_limit
            )

            if not rate_limit_result.allowed {
                return self.create_error_response(
                    429,
                    "Rate limit exceeded",
                    headers: {
                        "X-RateLimit-Reset": rate_limit_result.reset_time
                    }
                )
            }

            # Service discovery and routing
            let target_service = route_config.service
            let service_instance = self.service_registry.discover_service(target_service)

            # Circuit breaker pattern
            let circuit_breaker = self.get_circuit_breaker(target_service)

            let response = await circuit_breaker.execute(async func() {
                return await self.forward_request(request, service_instance, route_config)
            })

            return response

        } catch (error) {
            log_error("api_gateway_error", {
                "path": request.path,
                "method": request.method,
                "error": error.message,
                "request_id": request.id
            })

            return self.create_error_response(500, "Internal server error")
        }
    }

    match_route(path) {
        for route_pattern in self.routes {
            if self.path_matches_pattern(path, route_pattern) {
                return self.routes[route_pattern]
            }
        }
        return null
    }

    path_matches_pattern(path, pattern) {
        # Simple glob-style matching
        if pattern.endswith("/*") {
            let prefix = pattern[:-2]  # Remove /*
            return path.startswith(prefix)
        }

        return path == pattern
    }

    async forward_request(request, service_instance, route_config) {
        # Modify request for downstream service
        let forwarded_path = request.path
        if route_config.strip_prefix {
            forwarded_path = forwarded_path.replace(route_config.strip_prefix, "", 1)
        }

        let service_url = f"http://{service_instance.host}:{service_instance.port}{forwarded_path}"

        # Add gateway headers
        let headers = {**request.headers}
        headers["X-Gateway-Request-ID"] = request.id
        headers["X-Forwarded-For"] = request.client_ip
        headers["X-Forwarded-Proto"] = request.scheme

        if request.user {
            headers["X-User-ID"] = request.user.id
            headers["X-User-Roles"] = request.user.roles.join(",")
        }

        # Forward request
        let response = await http.request({
            "method": request.method,
            "url": service_url,
            "headers": headers,
            "json": request.body if request.method in ["POST", "PUT", "PATCH"] else null,
            "params": request.query_params,
            "timeout": 30
        })

        return {
            "status": response.status_code,
            "headers": response.headers,
            "body": response.text
        }
    }

    get_circuit_breaker(service_name) {
        if service_name not in self.circuit_breakers {
            self.circuit_breakers[service_name] = CircuitBreaker(
                service_name: service_name,
                failure_threshold: 5,
                recovery_timeout: 60,
                request_timeout: 30
            )
        }

        return self.circuit_breakers[service_name]
    }
}

// Saga pattern for distributed transactions
class OrderSaga {
    saga_manager: SagaManager
    compensation_actions: []

    constructor(saga_manager) {
        self.saga_manager = saga_manager
        self.compensation_actions = []
    }

    async execute_order_processing(order_data) {
        let saga_id = generate_saga_id()

        try {
            # Step 1: Reserve inventory
            let inventory_result = await self.execute_step(
                saga_id,
                "reserve_inventory",
                async func() {
                    return await self.inventory_service.reserve_items(order_data.items)
                },
                compensation: async func(result) {
                    await self.inventory_service.release_reservation(result.reservation_id)
                }
            )

            # Step 2: Process payment
            let payment_result = await self.execute_step(
                saga_id,
                "process_payment",
                async func() {
                    return await self.payment_service.charge_customer(
                        order_data.customer_id,
                        order_data.total_amount
                    )
                },
                compensation: async func(result) {
                    await self.payment_service.refund_payment(result.payment_id)
                }
            )

            # Step 3: Create order
            let order_result = await self.execute_step(
                saga_id,
                "create_order",
                async func() {
                    return await self.order_service.create_order({
                        **order_data,
                        "inventory_reservation_id": inventory_result.reservation_id,
                        "payment_id": payment_result.payment_id
                    })
                },
                compensation: async func(result) {
                    await self.order_service.cancel_order(result.order_id)
                }
            )

            # Step 4: Send confirmation
            await self.execute_step(
                saga_id,
                "send_confirmation",
                async func() {
                    return await self.notification_service.send_order_confirmation(
                        order_result.order_id,
                        order_data.customer_id
                    )
                }
                # No compensation needed for notification
            )

            await self.saga_manager.complete_saga(saga_id)

            return {
                "success": true,
                "order_id": order_result.order_id,
                "saga_id": saga_id
            }

        } catch (error) {
            print(f"Saga {saga_id} failed: {error.message}")
            await self.compensate_saga(saga_id)

            return {
                "success": false,
                "error": error.message,
                "saga_id": saga_id
            }
        }
    }

    async execute_step(saga_id, step_name, action, compensation = null) {
        try {
            let result = await action()

            # Record successful step
            await self.saga_manager.record_step(saga_id, step_name, "COMPLETED", result)

            # Store compensation action if provided
            if compensation {
                self.compensation_actions.append({
                    "saga_id": saga_id,
                    "step_name": step_name,
                    "compensation": compensation,
                    "result": result
                })
            }

            return result

        } catch (error) {
            await self.saga_manager.record_step(saga_id, step_name, "FAILED", {"error": error.message})
            throw error
        }
    }

    async compensate_saga(saga_id) {
        print(f"Starting compensation for saga {saga_id}")

        # Execute compensation actions in reverse order
        let compensations = self.compensation_actions.filter(c => c.saga_id == saga_id)
        compensations.reverse()

        for compensation in compensations {
            try {
                await compensation.compensation(compensation.result)
                print(f"Compensated step: {compensation.step_name}")

            } catch (compensation_error) {
                log_error("saga_compensation_failed", {
                    "saga_id": saga_id,
                    "step_name": compensation.step_name,
                    "error": compensation_error.message
                })

                # Continue with other compensations even if one fails
            }
        }

        await self.saga_manager.mark_saga_compensated(saga_id)
        print(f"Saga {saga_id} compensation completed")
    }
}

Section 4: Observability and Monitoring Architecture

Comprehensive Monitoring System

// Distributed tracing implementation
class TracingSystem {
    tracer: Tracer
    span_processor: SpanProcessor
    exporters: []

    constructor() {
        self.tracer = Tracer()
        self.span_processor = BatchSpanProcessor()
        self.exporters = [
            JaegerExporter("http://jaeger:14268/api/traces"),
            PrometheusExporter("http://prometheus:9090/api/v1/write")
        ]
    }

    start_span(operation_name, parent_span = null) {
        let span_context = self.create_span_context(parent_span)

        let span = Span(
            trace_id: span_context.trace_id,
            span_id: generate_span_id(),
            parent_span_id: span_context.parent_span_id,
            operation_name: operation_name,
            start_time: time.precise_now(),
            tags: {},
            logs: []
        )

        return span
    }

    create_span_context(parent_span) {
        if parent_span {
            return SpanContext(
                trace_id: parent_span.trace_id,
                parent_span_id: parent_span.span_id
            )
        } else {
            return SpanContext(
                trace_id: generate_trace_id(),
                parent_span_id: null
            )
        }
    }

    finish_span(span) {
        span.end_time = time.precise_now()
        span.duration = span.end_time - span.start_time

        self.span_processor.process_span(span)
    }

    // Context manager for automatic span lifecycle
    trace(operation_name, parent_span = null) {
        return TracingContext(self, operation_name, parent_span)
    }
}

class TracingContext {
    constructor(tracing_system, operation_name, parent_span) {
        self.tracing_system = tracing_system
        self.operation_name = operation_name
        self.parent_span = parent_span
        self.span = null
    }

    async __enter__() {
        self.span = self.tracing_system.start_span(self.operation_name, self.parent_span)
        return self.span
    }

    async __exit__(exc_type, exc_val, exc_tb) {
        if exc_type {
            self.span.set_tag("error", true)
            self.span.log("error", {
                "message": str(exc_val),
                "type": exc_type.__name__
            })
        }

        self.tracing_system.finish_span(self.span)
    }
}

// Metrics collection system
class MetricsCollector {
    metrics: {}
    labels: {}

    constructor() {
        self.metrics = {}
        self.labels = {}
    }

    counter(name, description = "", labels = {}) {
        if name not in self.metrics {
            self.metrics[name] = Counter(name, description)
        }

        return self.metrics[name].with_labels(labels)
    }

    gauge(name, description = "", labels = {}) {
        if name not in self.metrics {
            self.metrics[name] = Gauge(name, description)
        }

        return self.metrics[name].with_labels(labels)
    }

    histogram(name, description = "", buckets = null, labels = {}) {
        if name not in self.metrics {
            self.metrics[name] = Histogram(name, description, buckets)
        }

        return self.metrics[name].with_labels(labels)
    }

    # Business metrics helpers
    track_request(method, path, status_code, duration) {
        # Request counter
        self.counter("http_requests_total", "Total HTTP requests").labels({
            "method": method,
            "path": path,
            "status": str(status_code)
        }).inc()

        # Request duration
        self.histogram("http_request_duration_seconds", "HTTP request duration").labels({
            "method": method,
            "path": path
        }).observe(duration)

        # Error rate
        if status_code >= 400 {
            self.counter("http_errors_total", "Total HTTP errors").labels({
                "method": method,
                "path": path,
                "status": str(status_code)
            }).inc()
        }
    }

    track_database_operation(operation, table, duration, success) {
        self.counter("database_operations_total", "Total database operations").labels({
            "operation": operation,
            "table": table,
            "success": str(success)
        }).inc()

        self.histogram("database_operation_duration_seconds", "Database operation duration").labels({
            "operation": operation,
            "table": table
        }).observe(duration)
    }

    track_business_event(event_type, user_id = null, additional_labels = {}) {
        let labels = {"event_type": event_type, **additional_labels}
        if user_id {
            labels["user_id"] = user_id
        }

        self.counter("business_events_total", "Total business events").labels(labels).inc()
    }
}

// Structured logging system
class StructuredLogger {
    log_level: string
    formatters: []
    outputs: []

    constructor(log_level = "INFO") {
        self.log_level = log_level
        self.formatters = [JSONFormatter()]
        self.outputs = [ConsoleOutput(), FileOutput("app.log")]
    }

    log(level, message, context = {}) {
        if not self.should_log(level) {
            return
        }

        let log_entry = LogEntry(
            timestamp: datetime.now(),
            level: level,
            message: message,
            context: context,
            trace_id: self.get_current_trace_id(),
            span_id: self.get_current_span_id()
        )

        for formatter in self.formatters {
            let formatted_log = formatter.format(log_entry)

            for output in self.outputs {
                output.write(formatted_log)
            }
        }
    }

    info(message, context = {}) {
        self.log("INFO", message, context)
    }

    warning(message, context = {}) {
        self.log("WARNING", message, context)
    }

    error(message, context = {}) {
        self.log("ERROR", message, context)
    }

    debug(message, context = {}) {
        self.log("DEBUG", message, context)
    }

    # Business event logging
    log_user_action(user_id, action, details = {}) {
        self.info(f"User action: {action}", {
            "event_type": "user_action",
            "user_id": user_id,
            "action": action,
            "details": details
        })
    }

    log_system_event(event_type, component, details = {}) {
        self.info(f"System event: {event_type}", {
            "event_type": "system_event",
            "component": component,
            "details": details
        })
    }

    log_business_transaction(transaction_id, transaction_type, amount = null, details = {}) {
        self.info(f"Business transaction: {transaction_type}", {
            "event_type": "business_transaction",
            "transaction_id": transaction_id,
            "transaction_type": transaction_type,
            "amount": amount,
            "details": details
        })
    }
}

Andre's Architecture Wisdom

"Architecture isn't about following patterns blindlyβ€”it's about solving real problems with the right level of complexity. Start simple, measure what matters, and evolve your architecture as your understanding of the problem grows. The best architectures are those that your team can understand, maintain, and extend over time."

Architectural Decision Framework

  1. Start Simple: Begin with the simplest architecture that could work
  2. Measure First: Instrument everything from day one
  3. Evolve Gradually: Add complexity only when you have evidence it's needed
  4. Design for Change: Make it easy to refactor and evolve
  5. Consider Your Team: Architecture must match your team's capabilities

Capstone Project: E-Commerce Platform

Build a complete e-commerce platform using all the patterns from this chapter:

  • Clean architecture with domain-driven design
  • Event-driven communication between services
  • API gateway with authentication and rate limiting
  • Distributed transactions using sagas
  • Comprehensive observability and monitoring
  • Microservices with service discovery

Next Chapter Preview: Chapter 15 will begin our portfolio project section with building a comprehensive Web Application that demonstrates mastery of all concepts learned so far.


Remember: Great architecture emerges from understanding your domain deeply, measuring what matters, and evolving your design as you learn. The patterns you've learned here are toolsβ€”use them wisely to solve real problems.