+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 430 of 541

๐Ÿ“˜ Design Patterns: Advanced Patterns

Master design patterns: advanced patterns in Python with practical examples, best practices, and real-world applications ๐Ÿš€

๐Ÿ’ŽAdvanced
35 min read

Prerequisites

  • Basic understanding of programming concepts ๐Ÿ“
  • Python installation (3.8+) ๐Ÿ
  • VS Code or preferred IDE ๐Ÿ’ป

What you'll learn

  • Understand the concept fundamentals ๐ŸŽฏ
  • Apply the concept in real projects ๐Ÿ—๏ธ
  • Debug common issues ๐Ÿ›
  • Write clean, Pythonic code โœจ

๐ŸŽฏ Introduction

Welcome to this exciting deep dive into advanced design patterns in Python! ๐ŸŽ‰ In this guide, weโ€™ll explore sophisticated patterns that go beyond the classic Gang of Four patterns, taking your Python skills to the next level.

Youโ€™ll discover how advanced design patterns can transform your code architecture, making it more flexible, maintainable, and elegant. Whether youโ€™re building microservices ๐ŸŒ, event-driven systems ๐Ÿ”„, or complex applications ๐Ÿ—๏ธ, mastering these patterns is essential for writing professional-grade Python code.

By the end of this tutorial, youโ€™ll feel confident implementing advanced patterns like the Repository pattern, CQRS, Event Sourcing, and more in your own projects! Letโ€™s dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ“š Understanding Advanced Design Patterns

๐Ÿค” What are Advanced Design Patterns?

Advanced design patterns are like sophisticated blueprints for solving complex architectural problems ๐ŸŽจ. Think of them as specialized tools in a master craftsmanโ€™s workshop - each designed for specific, intricate tasks that basic tools canโ€™t handle elegantly.

In Python terms, these patterns help you build systems that can scale, adapt to change, and handle complex business logic with grace. This means you can:

  • โœจ Build systems that scale effortlessly
  • ๐Ÿš€ Separate concerns for cleaner architecture
  • ๐Ÿ›ก๏ธ Handle complex domain logic elegantly
  • ๐Ÿ”„ Create event-driven architectures
  • ๐Ÿ“Š Implement sophisticated data access patterns

๐Ÿ’ก Why Use Advanced Patterns?

Hereโ€™s why senior developers love advanced patterns:

  1. Scalability ๐Ÿš€: Design systems that grow with your needs
  2. Maintainability ๐Ÿ”ง: Keep complex systems manageable
  3. Testability ๐Ÿงช: Write unit tests with ease
  4. Flexibility ๐ŸŽฏ: Adapt to changing requirements
  5. Performance โšก: Optimize critical paths effectively

Real-world example: Imagine building an e-commerce platform ๐Ÿ›’. With advanced patterns like Repository and CQRS, you can separate your read and write operations, implement caching strategies, and handle millions of transactions efficiently!

๐Ÿ”ง Basic Syntax and Usage

๐Ÿ“ Repository Pattern

Letโ€™s start with the Repository pattern - your data access superhero:

# ๐Ÿ‘‹ Hello, Repository Pattern!
from abc import ABC, abstractmethod
from typing import List, Optional, Dict, Any
import json

# ๐ŸŽจ Define our domain model
class Product:
    def __init__(self, id: str, name: str, price: float, stock: int):
        self.id = id
        self.name = name
        self.price = price
        self.stock = stock
        self.emoji = "๐Ÿ“ฆ"  # Every product needs an emoji! 
    
    def __repr__(self):
        return f"{self.emoji} Product({self.name}, ${self.price})"

# ๐Ÿ›ก๏ธ Abstract repository interface
class Repository(ABC):
    @abstractmethod
    def add(self, entity: Any) -> None:
        pass
    
    @abstractmethod
    def get(self, id: str) -> Optional[Any]:
        pass
    
    @abstractmethod
    def get_all(self) -> List[Any]:
        pass
    
    @abstractmethod
    def update(self, entity: Any) -> None:
        pass
    
    @abstractmethod
    def delete(self, id: str) -> None:
        pass

# ๐Ÿš€ Concrete implementation
class ProductRepository(Repository):
    def __init__(self):
        self._storage: Dict[str, Product] = {}
        print("๐Ÿช Product repository initialized!")
    
    def add(self, product: Product) -> None:
        self._storage[product.id] = product
        print(f"โœ… Added {product.emoji} {product.name} to repository!")
    
    def get(self, id: str) -> Optional[Product]:
        return self._storage.get(id)
    
    def get_all(self) -> List[Product]:
        return list(self._storage.values())
    
    def update(self, product: Product) -> None:
        if product.id in self._storage:
            self._storage[product.id] = product
            print(f"๐Ÿ“ Updated {product.emoji} {product.name}")
    
    def delete(self, id: str) -> None:
        if id in self._storage:
            product = self._storage.pop(id)
            print(f"๐Ÿ—‘๏ธ Deleted {product.emoji} {product.name}")

๐Ÿ’ก Explanation: The Repository pattern abstracts data access, making it easy to switch between different storage mechanisms (database, file, memory) without changing your business logic!

๐ŸŽฏ Unit of Work Pattern

Now letโ€™s combine it with Unit of Work for transaction management:

# ๐Ÿ—๏ธ Unit of Work pattern for transaction management
class UnitOfWork:
    def __init__(self):
        self._products = ProductRepository()
        self._new: List[Product] = []
        self._dirty: List[Product] = []
        self._removed: List[str] = []
        print("๐Ÿ’ผ Unit of Work initialized!")
    
    # ๐ŸŽจ Track new entities
    def register_new(self, product: Product):
        self._new.append(product)
        print(f"๐Ÿ“ Registered new: {product.emoji} {product.name}")
    
    # ๐Ÿ”„ Track modified entities
    def register_dirty(self, product: Product):
        if product not in self._dirty:
            self._dirty.append(product)
            print(f"โœ๏ธ Registered dirty: {product.emoji} {product.name}")
    
    # ๐Ÿ—‘๏ธ Track removed entities
    def register_removed(self, id: str):
        self._removed.append(id)
        print(f"๐Ÿ—‘๏ธ Registered for removal: {id}")
    
    # ๐Ÿ’พ Commit all changes
    def commit(self):
        print("๐Ÿš€ Committing changes...")
        
        # Add new products
        for product in self._new:
            self._products.add(product)
        
        # Update modified products
        for product in self._dirty:
            self._products.update(product)
        
        # Remove deleted products
        for id in self._removed:
            self._products.delete(id)
        
        # Clear tracking lists
        self._new.clear()
        self._dirty.clear()
        self._removed.clear()
        
        print("โœ… All changes committed!")
    
    # ๐Ÿ”„ Rollback changes
    def rollback(self):
        self._new.clear()
        self._dirty.clear()
        self._removed.clear()
        print("โ†ฉ๏ธ Changes rolled back!")

๐Ÿ’ก Practical Examples

๐Ÿ›’ Example 1: E-commerce with CQRS Pattern

Letโ€™s build a CQRS (Command Query Responsibility Segregation) system:

# ๐Ÿ›๏ธ CQRS Pattern - Separate read and write models
from dataclasses import dataclass
from datetime import datetime
from typing import List, Dict
import uuid

# ๐Ÿ“ Commands (write operations)
@dataclass
class Command:
    timestamp: datetime = None
    
    def __post_init__(self):
        if self.timestamp is None:
            self.timestamp = datetime.now()

@dataclass
class CreateOrderCommand(Command):
    customer_id: str
    items: List[Dict[str, Any]]
    emoji: str = "๐Ÿ›’"

@dataclass
class UpdateOrderStatusCommand(Command):
    order_id: str
    status: str
    emoji: str = "๐Ÿ“ฆ"

# ๐Ÿ” Queries (read operations)
@dataclass
class Query:
    pass

@dataclass
class GetOrderByIdQuery(Query):
    order_id: str

@dataclass
class GetOrdersByCustomerQuery(Query):
    customer_id: str

# ๐ŸŽฏ Command handlers
class CommandHandler:
    def __init__(self):
        self.events = []  # Event store
        print("โšก Command handler ready!")
    
    def handle(self, command: Command):
        if isinstance(command, CreateOrderCommand):
            order_id = str(uuid.uuid4())
            event = {
                'type': 'OrderCreated',
                'order_id': order_id,
                'customer_id': command.customer_id,
                'items': command.items,
                'timestamp': command.timestamp,
                'emoji': command.emoji
            }
            self.events.append(event)
            print(f"{command.emoji} Order {order_id} created!")
            return order_id
        
        elif isinstance(command, UpdateOrderStatusCommand):
            event = {
                'type': 'OrderStatusUpdated',
                'order_id': command.order_id,
                'status': command.status,
                'timestamp': command.timestamp,
                'emoji': command.emoji
            }
            self.events.append(event)
            print(f"{command.emoji} Order {command.order_id} status: {command.status}")

# ๐Ÿ“Š Query handlers with read model
class QueryHandler:
    def __init__(self, events):
        self.events = events
        self._orders = {}  # Read model
        self._rebuild_read_model()
        print("๐Ÿ” Query handler ready!")
    
    def _rebuild_read_model(self):
        # Rebuild read model from events
        for event in self.events:
            if event['type'] == 'OrderCreated':
                self._orders[event['order_id']] = {
                    'id': event['order_id'],
                    'customer_id': event['customer_id'],
                    'items': event['items'],
                    'status': 'created',
                    'created_at': event['timestamp']
                }
            elif event['type'] == 'OrderStatusUpdated':
                if event['order_id'] in self._orders:
                    self._orders[event['order_id']]['status'] = event['status']
    
    def handle(self, query: Query):
        if isinstance(query, GetOrderByIdQuery):
            return self._orders.get(query.order_id)
        
        elif isinstance(query, GetOrdersByCustomerQuery):
            return [
                order for order in self._orders.values()
                if order['customer_id'] == query.customer_id
            ]

# ๐ŸŽฎ Let's use CQRS!
command_handler = CommandHandler()
query_handler = QueryHandler(command_handler.events)

# Create an order
create_cmd = CreateOrderCommand(
    customer_id="cust-123",
    items=[
        {"name": "Python Book", "price": 29.99, "emoji": "๐Ÿ“˜"},
        {"name": "Coffee", "price": 4.99, "emoji": "โ˜•"}
    ]
)
order_id = command_handler.handle(create_cmd)

# Update order status
update_cmd = UpdateOrderStatusCommand(order_id=order_id, status="shipped")
command_handler.handle(update_cmd)

# Query the order
query_handler = QueryHandler(command_handler.events)  # Rebuild read model
order = query_handler.handle(GetOrderByIdQuery(order_id=order_id))
print(f"๐Ÿ“‹ Order details: {order}")

๐ŸŽฏ Try it yourself: Add a cancel order command and implement event replay functionality!

๐ŸŽฎ Example 2: Event Sourcing Pattern

Letโ€™s implement event sourcing for game state:

# ๐Ÿ† Event Sourcing for a game
from abc import ABC, abstractmethod
from datetime import datetime
from typing import List, Dict, Any
import json

# ๐ŸŽฏ Base event class
class Event(ABC):
    def __init__(self, aggregate_id: str):
        self.aggregate_id = aggregate_id
        self.timestamp = datetime.now()
        self.version = 1
    
    @abstractmethod
    def apply(self, state: Dict[str, Any]) -> Dict[str, Any]:
        pass

# ๐ŸŽฎ Game events
class GameStartedEvent(Event):
    def __init__(self, player_id: str, player_name: str):
        super().__init__(player_id)
        self.player_name = player_name
        self.emoji = "๐ŸŽฎ"
    
    def apply(self, state: Dict[str, Any]) -> Dict[str, Any]:
        return {
            **state,
            'player_id': self.aggregate_id,
            'player_name': self.player_name,
            'score': 0,
            'level': 1,
            'achievements': ["๐ŸŒŸ First Steps"],
            'status': 'playing'
        }

class PointsEarnedEvent(Event):
    def __init__(self, player_id: str, points: int):
        super().__init__(player_id)
        self.points = points
        self.emoji = "โœจ"
    
    def apply(self, state: Dict[str, Any]) -> Dict[str, Any]:
        new_score = state['score'] + self.points
        new_level = (new_score // 100) + 1
        
        new_state = {
            **state,
            'score': new_score,
            'level': new_level
        }
        
        # ๐ŸŽŠ Level up achievement
        if new_level > state['level']:
            new_state['achievements'] = state['achievements'] + [f"๐Ÿ† Level {new_level} Master"]
        
        return new_state

class PowerUpCollectedEvent(Event):
    def __init__(self, player_id: str, power_up: str):
        super().__init__(player_id)
        self.power_up = power_up
        self.emoji = "๐Ÿ’ช"
    
    def apply(self, state: Dict[str, Any]) -> Dict[str, Any]:
        power_ups = state.get('power_ups', [])
        return {
            **state,
            'power_ups': power_ups + [self.power_up]
        }

# ๐Ÿ“š Event store
class EventStore:
    def __init__(self):
        self._events: Dict[str, List[Event]] = {}
        print("๐Ÿ’พ Event store initialized!")
    
    def save_event(self, event: Event):
        if event.aggregate_id not in self._events:
            self._events[event.aggregate_id] = []
        
        # Set version number
        event.version = len(self._events[event.aggregate_id]) + 1
        self._events[event.aggregate_id].append(event)
        
        print(f"{getattr(event, 'emoji', '๐Ÿ“')} Event saved: {event.__class__.__name__} v{event.version}")
    
    def get_events(self, aggregate_id: str) -> List[Event]:
        return self._events.get(aggregate_id, [])

# ๐ŸŽฎ Game aggregate
class GamePlayer:
    def __init__(self, event_store: EventStore):
        self.event_store = event_store
        self._state: Dict[str, Any] = {}
    
    def start_game(self, player_id: str, player_name: str):
        event = GameStartedEvent(player_id, player_name)
        self.event_store.save_event(event)
        print(f"๐ŸŽฎ {player_name} started playing!")
    
    def earn_points(self, player_id: str, points: int):
        event = PointsEarnedEvent(player_id, points)
        self.event_store.save_event(event)
        
        # Get current state to show level up
        state = self.get_player_state(player_id)
        print(f"โœจ Earned {points} points! Score: {state['score']}")
    
    def collect_power_up(self, player_id: str, power_up: str):
        event = PowerUpCollectedEvent(player_id, power_up)
        self.event_store.save_event(event)
        print(f"๐Ÿ’ช Collected power-up: {power_up}")
    
    def get_player_state(self, player_id: str) -> Dict[str, Any]:
        # Rebuild state from events
        events = self.event_store.get_events(player_id)
        state = {}
        
        for event in events:
            state = event.apply(state)
        
        return state
    
    def replay_game_history(self, player_id: str):
        print(f"\n๐ŸŽฌ Replaying game history for player {player_id}:")
        events = self.event_store.get_events(player_id)
        
        state = {}
        for i, event in enumerate(events):
            state = event.apply(state)
            print(f"  ๐Ÿ“น Event {i+1}: {event.__class__.__name__} - Score: {state.get('score', 0)}")

# ๐ŸŽฎ Let's play!
event_store = EventStore()
game = GamePlayer(event_store)

# Start a new game
game.start_game("player-1", "Alice")

# Play the game
game.earn_points("player-1", 50)
game.collect_power_up("player-1", "๐Ÿš€ Speed Boost")
game.earn_points("player-1", 75)
game.collect_power_up("player-1", "๐Ÿ›ก๏ธ Shield")
game.earn_points("player-1", 100)

# Check final state
final_state = game.get_player_state("player-1")
print(f"\n๐Ÿ“Š Final state: Level {final_state['level']}, Score: {final_state['score']}")
print(f"๐Ÿ† Achievements: {', '.join(final_state['achievements'])}")
print(f"๐Ÿ’ช Power-ups: {', '.join(final_state.get('power_ups', []))}")

# Replay history
game.replay_game_history("player-1")

๐Ÿš€ Advanced Concepts

๐Ÿง™โ€โ™‚๏ธ Specification Pattern

When youโ€™re ready to level up, try the Specification pattern for complex business rules:

# ๐ŸŽฏ Specification pattern for complex business rules
from abc import ABC, abstractmethod
from typing import Any, List

class Specification(ABC):
    @abstractmethod
    def is_satisfied_by(self, candidate: Any) -> bool:
        pass
    
    def and_(self, other: 'Specification') -> 'AndSpecification':
        return AndSpecification(self, other)
    
    def or_(self, other: 'Specification') -> 'OrSpecification':
        return OrSpecification(self, other)
    
    def not_(self) -> 'NotSpecification':
        return NotSpecification(self)

# ๐Ÿ”— Composite specifications
class AndSpecification(Specification):
    def __init__(self, left: Specification, right: Specification):
        self.left = left
        self.right = right
    
    def is_satisfied_by(self, candidate: Any) -> bool:
        return self.left.is_satisfied_by(candidate) and self.right.is_satisfied_by(candidate)

class OrSpecification(Specification):
    def __init__(self, left: Specification, right: Specification):
        self.left = left
        self.right = right
    
    def is_satisfied_by(self, candidate: Any) -> bool:
        return self.left.is_satisfied_by(candidate) or self.right.is_satisfied_by(candidate)

class NotSpecification(Specification):
    def __init__(self, spec: Specification):
        self.spec = spec
    
    def is_satisfied_by(self, candidate: Any) -> bool:
        return not self.spec.is_satisfied_by(candidate)

# ๐Ÿ›๏ธ Business rule specifications
class PremiumCustomerSpec(Specification):
    def is_satisfied_by(self, customer: Dict[str, Any]) -> bool:
        return customer.get('tier') == 'premium'

class HighValueOrderSpec(Specification):
    def __init__(self, threshold: float):
        self.threshold = threshold
    
    def is_satisfied_by(self, order: Dict[str, Any]) -> bool:
        return order.get('total', 0) >= self.threshold

class RecentCustomerSpec(Specification):
    def __init__(self, days: int):
        self.days = days
    
    def is_satisfied_by(self, customer: Dict[str, Any]) -> bool:
        # Simplified: check if registered within days
        return customer.get('days_since_registration', float('inf')) <= self.days

# ๐ŸŽจ Using specifications
premium_spec = PremiumCustomerSpec()
high_value_spec = HighValueOrderSpec(100.0)
recent_spec = RecentCustomerSpec(30)

# Complex business rule: Premium OR (High value AND Recent)
eligible_for_discount = premium_spec.or_(high_value_spec.and_(recent_spec))

# Test customers
customers = [
    {"name": "Alice", "tier": "premium", "order_total": 50, "days_since_registration": 60},
    {"name": "Bob", "tier": "regular", "order_total": 150, "days_since_registration": 15},
    {"name": "Charlie", "tier": "regular", "order_total": 30, "days_since_registration": 5},
]

for customer in customers:
    order = {"total": customer["order_total"]}
    if eligible_for_discount.is_satisfied_by(customer) and high_value_spec.is_satisfied_by(order):
        print(f"โœ… {customer['name']} is eligible for discount! ๐ŸŽ‰")
    else:
        print(f"โŒ {customer['name']} is not eligible")

๐Ÿ—๏ธ Saga Pattern for Distributed Transactions

For the brave developers - implement distributed transactions:

# ๐Ÿš€ Saga pattern for distributed transactions
from enum import Enum
from typing import List, Callable, Any, Optional
import uuid

class SagaStatus(Enum):
    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    COMPENSATING = "compensating"
    FAILED = "failed"

class SagaStep:
    def __init__(self, name: str, action: Callable, compensation: Callable):
        self.name = name
        self.action = action
        self.compensation = compensation
        self.status = SagaStatus.PENDING
        self.result = None
        self.error = None
        self.emoji = "๐Ÿ“"
    
    def execute(self) -> bool:
        try:
            print(f"โ–ถ๏ธ Executing: {self.name}")
            self.status = SagaStatus.RUNNING
            self.result = self.action()
            self.status = SagaStatus.COMPLETED
            print(f"โœ… Completed: {self.name}")
            return True
        except Exception as e:
            self.status = SagaStatus.FAILED
            self.error = str(e)
            print(f"โŒ Failed: {self.name} - {e}")
            return False
    
    def compensate(self) -> bool:
        if self.status != SagaStatus.COMPLETED:
            return True  # Nothing to compensate
        
        try:
            print(f"โ†ฉ๏ธ Compensating: {self.name}")
            self.status = SagaStatus.COMPENSATING
            self.compensation(self.result)
            print(f"โœ… Compensated: {self.name}")
            return True
        except Exception as e:
            print(f"โš ๏ธ Compensation failed: {self.name} - {e}")
            return False

class Saga:
    def __init__(self, name: str):
        self.id = str(uuid.uuid4())
        self.name = name
        self.steps: List[SagaStep] = []
        self.status = SagaStatus.PENDING
        print(f"๐Ÿ“‹ Saga '{name}' created with ID: {self.id}")
    
    def add_step(self, step: SagaStep):
        self.steps.append(step)
        print(f"โž• Added step: {step.name}")
    
    def execute(self):
        print(f"\n๐Ÿš€ Starting saga: {self.name}")
        self.status = SagaStatus.RUNNING
        
        completed_steps = []
        
        for step in self.steps:
            if step.execute():
                completed_steps.append(step)
            else:
                # Step failed, compensate completed steps
                print(f"\nโš ๏ธ Saga failed at step: {step.name}")
                self.status = SagaStatus.COMPENSATING
                
                # Compensate in reverse order
                for completed_step in reversed(completed_steps):
                    completed_step.compensate()
                
                self.status = SagaStatus.FAILED
                print(f"โŒ Saga '{self.name}' failed and compensated")
                return False
        
        self.status = SagaStatus.COMPLETED
        print(f"\n๐ŸŽ‰ Saga '{self.name}' completed successfully!")
        return True

# ๐Ÿ›’ Example: Order processing saga
class OrderService:
    def __init__(self):
        self.orders = {}
    
    def create_order(self, order_id: str, items: List[str]) -> str:
        self.orders[order_id] = {"id": order_id, "items": items, "status": "created"}
        print(f"  ๐Ÿ“ฆ Order {order_id} created")
        return order_id
    
    def cancel_order(self, order_id: str):
        if order_id in self.orders:
            self.orders[order_id]["status"] = "cancelled"
            print(f"  ๐Ÿ—‘๏ธ Order {order_id} cancelled")

class PaymentService:
    def __init__(self):
        self.payments = {}
        self.balance = 1000  # Starting balance
    
    def process_payment(self, payment_id: str, amount: float) -> str:
        if amount > self.balance:
            raise Exception("Insufficient funds! ๐Ÿ’ธ")
        
        self.balance -= amount
        self.payments[payment_id] = {"id": payment_id, "amount": amount, "status": "processed"}
        print(f"  ๐Ÿ’ณ Payment {payment_id} processed: ${amount}")
        return payment_id
    
    def refund_payment(self, payment_id: str):
        if payment_id in self.payments:
            amount = self.payments[payment_id]["amount"]
            self.balance += amount
            self.payments[payment_id]["status"] = "refunded"
            print(f"  ๐Ÿ’ฐ Payment {payment_id} refunded: ${amount}")

class InventoryService:
    def __init__(self):
        self.inventory = {"item1": 10, "item2": 5, "item3": 0}
    
    def reserve_items(self, reservation_id: str, items: List[str]) -> str:
        for item in items:
            if self.inventory.get(item, 0) <= 0:
                raise Exception(f"Item {item} out of stock! ๐Ÿ“ฆ")
            
            self.inventory[item] -= 1
        
        print(f"  ๐Ÿ“‹ Items reserved: {items}")
        return reservation_id
    
    def release_items(self, reservation_id: str):
        # Simplified: just add items back
        print(f"  ๐Ÿ“ค Items released for reservation {reservation_id}")

# ๐ŸŽฎ Create and execute saga
order_service = OrderService()
payment_service = PaymentService()
inventory_service = InventoryService()

# Create order processing saga
saga = Saga("Process Online Order")

# Add saga steps
saga.add_step(SagaStep(
    "Create Order",
    lambda: order_service.create_order("order-123", ["item1", "item2"]),
    lambda order_id: order_service.cancel_order(order_id)
))

saga.add_step(SagaStep(
    "Reserve Inventory",
    lambda: inventory_service.reserve_items("reservation-123", ["item1", "item2"]),
    lambda reservation_id: inventory_service.release_items(reservation_id)
))

saga.add_step(SagaStep(
    "Process Payment",
    lambda: payment_service.process_payment("payment-123", 99.99),
    lambda payment_id: payment_service.refund_payment(payment_id)
))

# Execute the saga
saga.execute()

# Try a failing saga
print("\n" + "="*50 + "\n")

failing_saga = Saga("Process Large Order")

failing_saga.add_step(SagaStep(
    "Create Large Order",
    lambda: order_service.create_order("order-456", ["item3"]),  # Out of stock!
    lambda order_id: order_service.cancel_order(order_id)
))

failing_saga.add_step(SagaStep(
    "Reserve Out-of-Stock Items",
    lambda: inventory_service.reserve_items("reservation-456", ["item3"]),  # Will fail!
    lambda reservation_id: inventory_service.release_items(reservation_id)
))

failing_saga.add_step(SagaStep(
    "Process Large Payment",
    lambda: payment_service.process_payment("payment-456", 5000),  # Too expensive!
    lambda payment_id: payment_service.refund_payment(payment_id)
))

failing_saga.execute()

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Over-Engineering

# โŒ Wrong way - too complex for simple needs!
class OverEngineeredUserService:
    def __init__(self, 
                 repository_factory,
                 unit_of_work_factory,
                 event_store,
                 command_bus,
                 query_bus,
                 saga_orchestrator,
                 specification_engine):
        # ๐Ÿ˜ฐ This is overkill for basic CRUD!
        pass

# โœ… Correct way - start simple!
class SimpleUserService:
    def __init__(self, db_connection):
        self.db = db_connection
        print("โœจ Simple and effective!")
    
    def create_user(self, name: str, email: str):
        # Direct and clean
        return self.db.insert("users", {"name": name, "email": email})

๐Ÿคฏ Pitfall 2: Ignoring Transaction Boundaries

# โŒ Dangerous - no transaction management!
def transfer_money_unsafe(from_account: str, to_account: str, amount: float):
    # What if this fails after debit? ๐Ÿ’ฅ
    debit_account(from_account, amount)
    credit_account(to_account, amount)  # System crash here = money lost!

# โœ… Safe - use Unit of Work or Saga!
def transfer_money_safe(uow: UnitOfWork, from_account: str, to_account: str, amount: float):
    try:
        uow.accounts.debit(from_account, amount)
        uow.accounts.credit(to_account, amount)
        uow.commit()  # โœ… All or nothing!
        print("๐Ÿ’ฐ Transfer successful!")
    except Exception as e:
        uow.rollback()  # ๐Ÿ›ก๏ธ Safe rollback
        print(f"โš ๏ธ Transfer failed: {e}")

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Choose Wisely: Not every problem needs an advanced pattern!
  2. ๐Ÿ“ Document Intent: Explain WHY you chose a pattern
  3. ๐Ÿงช Test Thoroughly: Advanced patterns need comprehensive tests
  4. ๐ŸŽจ Keep It Pythonic: Donโ€™t fight the language
  5. โœจ Iterate: Start simple, evolve as needed
  6. ๐Ÿ” Monitor: Track pattern effectiveness in production

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Microservices Communication System

Create a complete system using multiple advanced patterns:

๐Ÿ“‹ Requirements:

  • โœ… Implement message bus for service communication
  • ๐Ÿท๏ธ Use Repository pattern for data access
  • ๐Ÿ‘ค Apply CQRS for read/write separation
  • ๐Ÿ“… Add event sourcing for audit trail
  • ๐ŸŽจ Include saga for distributed transactions
  • ๐Ÿ›ก๏ธ Use specification pattern for business rules

๐Ÿš€ Bonus Points:

  • Add circuit breaker pattern for resilience
  • Implement bulkhead pattern for isolation
  • Create a service mesh simulation

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
# ๐ŸŽฏ Microservices communication system!
from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import datetime
from typing import Dict, List, Callable, Any, Optional
from enum import Enum
import uuid
import time

# ๐Ÿ“จ Message bus for inter-service communication
class Message(ABC):
    def __init__(self):
        self.id = str(uuid.uuid4())
        self.timestamp = datetime.now()
        self.emoji = "๐Ÿ“จ"

@dataclass
class OrderPlacedMessage(Message):
    order_id: str
    customer_id: str
    total: float
    
    def __init__(self, order_id: str, customer_id: str, total: float):
        super().__init__()
        self.order_id = order_id
        self.customer_id = customer_id
        self.total = total
        self.emoji = "๐Ÿ›’"

class MessageBus:
    def __init__(self):
        self._handlers: Dict[type, List[Callable]] = {}
        print("๐ŸšŒ Message bus initialized!")
    
    def subscribe(self, message_type: type, handler: Callable):
        if message_type not in self._handlers:
            self._handlers[message_type] = []
        self._handlers[message_type].append(handler)
        print(f"๐Ÿ“Œ Subscribed to {message_type.__name__}")
    
    def publish(self, message: Message):
        message_type = type(message)
        handlers = self._handlers.get(message_type, [])
        
        print(f"{message.emoji} Publishing {message_type.__name__}")
        for handler in handlers:
            try:
                handler(message)
            except Exception as e:
                print(f"โš ๏ธ Handler error: {e}")

# ๐Ÿ—๏ธ Service base with repository and CQRS
class ServiceBase:
    def __init__(self, name: str, message_bus: MessageBus):
        self.name = name
        self.message_bus = message_bus
        self.event_store = []
        self.read_model = {}
        print(f"๐Ÿข {name} service initialized!")
    
    def record_event(self, event: Dict[str, Any]):
        event['service'] = self.name
        event['timestamp'] = datetime.now()
        self.event_store.append(event)
        self._update_read_model(event)
    
    def _update_read_model(self, event: Dict[str, Any]):
        # Override in subclasses
        pass

# ๐Ÿ›๏ธ Order service
class OrderService(ServiceBase):
    def __init__(self, message_bus: MessageBus):
        super().__init__("OrderService", message_bus)
        self.orders = {}  # Repository
    
    def place_order(self, customer_id: str, items: List[Dict[str, Any]]) -> str:
        order_id = f"order-{uuid.uuid4().hex[:8]}"
        total = sum(item['price'] * item['quantity'] for item in items)
        
        # Command: Create order
        order = {
            'id': order_id,
            'customer_id': customer_id,
            'items': items,
            'total': total,
            'status': 'pending'
        }
        self.orders[order_id] = order
        
        # Record event
        self.record_event({
            'type': 'OrderPlaced',
            'order': order
        })
        
        # Publish message
        self.message_bus.publish(
            OrderPlacedMessage(order_id, customer_id, total)
        )
        
        print(f"๐Ÿ“ฆ Order {order_id} placed! Total: ${total}")
        return order_id
    
    def _update_read_model(self, event: Dict[str, Any]):
        if event['type'] == 'OrderPlaced':
            order = event['order']
            self.read_model[order['id']] = {
                'id': order['id'],
                'customer_id': order['customer_id'],
                'total': order['total'],
                'item_count': len(order['items'])
            }

# ๐Ÿ’ณ Payment service with saga
class PaymentService(ServiceBase):
    def __init__(self, message_bus: MessageBus):
        super().__init__("PaymentService", message_bus)
        self.payments = {}
        self.customer_balances = {
            'cust-123': 500.0,
            'cust-456': 100.0
        }
        
        # Subscribe to order events
        message_bus.subscribe(OrderPlacedMessage, self.handle_order_placed)
    
    def handle_order_placed(self, message: OrderPlacedMessage):
        print(f"๐Ÿ’ณ Processing payment for order {message.order_id}")
        
        # Check if customer can afford it
        balance = self.customer_balances.get(message.customer_id, 0)
        
        if balance >= message.total:
            # Process payment
            payment_id = f"pay-{uuid.uuid4().hex[:8]}"
            self.payments[payment_id] = {
                'id': payment_id,
                'order_id': message.order_id,
                'amount': message.total,
                'status': 'completed'
            }
            self.customer_balances[message.customer_id] -= message.total
            
            self.record_event({
                'type': 'PaymentCompleted',
                'payment_id': payment_id,
                'order_id': message.order_id,
                'amount': message.total
            })
            
            print(f"โœ… Payment {payment_id} completed!")
        else:
            print(f"โŒ Insufficient balance for customer {message.customer_id}")
            
            self.record_event({
                'type': 'PaymentFailed',
                'order_id': message.order_id,
                'reason': 'Insufficient balance'
            })

# ๐Ÿ” Query service for reporting
class QueryService:
    def __init__(self, services: List[ServiceBase]):
        self.services = services
        print("๐Ÿ” Query service ready!")
    
    def get_customer_orders(self, customer_id: str) -> List[Dict[str, Any]]:
        orders = []
        for service in self.services:
            if isinstance(service, OrderService):
                for order_id, order_data in service.read_model.items():
                    if order_data['customer_id'] == customer_id:
                        orders.append(order_data)
        return orders
    
    def get_service_events(self, service_name: str) -> List[Dict[str, Any]]:
        for service in self.services:
            if service.name == service_name:
                return service.event_store
        return []

# ๐Ÿ›ก๏ธ Specification for business rules
class CustomerCreditSpec(Specification):
    def __init__(self, min_balance: float):
        self.min_balance = min_balance
    
    def is_satisfied_by(self, customer: Dict[str, Any]) -> bool:
        return customer.get('balance', 0) >= self.min_balance

# ๐ŸŽฎ Run the microservices system!
message_bus = MessageBus()
order_service = OrderService(message_bus)
payment_service = PaymentService(message_bus)
query_service = QueryService([order_service, payment_service])

# Place some orders
print("\n๐Ÿš€ Starting microservices demo...\n")

# Customer with sufficient balance
order1 = order_service.place_order('cust-123', [
    {'name': 'Microservices Book', 'price': 49.99, 'quantity': 1, 'emoji': '๐Ÿ“˜'},
    {'name': 'Coffee', 'price': 4.99, 'quantity': 2, 'emoji': 'โ˜•'}
])

time.sleep(0.1)  # Simulate async processing

# Customer with insufficient balance
order2 = order_service.place_order('cust-456', [
    {'name': 'Cloud Architecture Course', 'price': 299.99, 'quantity': 1, 'emoji': 'โ˜๏ธ'}
])

# Query customer orders
print(f"\n๐Ÿ“Š Customer cust-123 orders:")
for order in query_service.get_customer_orders('cust-123'):
    print(f"  ๐Ÿ“ฆ Order {order['id']}: ${order['total']} ({order['item_count']} items)")

# Show event sourcing
print(f"\n๐ŸŽฌ Payment service events:")
for event in query_service.get_service_events("PaymentService"):
    print(f"  ๐Ÿ“ {event['type']} at {event['timestamp'].strftime('%H:%M:%S')}")

๐ŸŽ“ Key Takeaways

Youโ€™ve mastered advanced design patterns! Hereโ€™s what you can now do:

  • โœ… Implement Repository pattern for clean data access ๐Ÿ’ช
  • โœ… Apply CQRS to separate read and write concerns ๐Ÿ›ก๏ธ
  • โœ… Use Event Sourcing for complete audit trails ๐ŸŽฏ
  • โœ… Build Sagas for distributed transactions ๐Ÿ›
  • โœ… Create flexible systems with advanced patterns! ๐Ÿš€

Remember: Advanced patterns are powerful tools, but use them wisely! Start simple and evolve your architecture as needed. ๐Ÿค

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve mastered advanced design patterns!

Hereโ€™s what to do next:

  1. ๐Ÿ’ป Practice implementing these patterns in your projects
  2. ๐Ÿ—๏ธ Build a small microservices system using CQRS and Event Sourcing
  3. ๐Ÿ“š Move on to our next tutorial: Parallel Programming Foundations
  4. ๐ŸŒŸ Share your architectural insights with the community!

Remember: Every architect started with simple patterns. Keep building, keep learning, and most importantly, design systems that solve real problems! ๐Ÿš€


Happy architecting! ๐ŸŽ‰๐Ÿš€โœจ