# Unified Messaging Architecture This document describes the standardized messaging system used across all bakery-ia microservices. ## Overview The unified messaging architecture provides a consistent approach for: - Publishing business events (inventory changes, user actions, etc.) - Publishing user-facing alerts, notifications, and recommendations - Consuming events from other services - Maintaining service-to-service communication patterns ## Core Components ### 1. UnifiedEventPublisher The main publisher for all event types, located in `shared/messaging/messaging_client.py`: ```python from shared.messaging import UnifiedEventPublisher, EVENT_TYPES, RabbitMQClient # Initialize rabbitmq_client = RabbitMQClient(settings.RABBITMQ_URL, service_name="my-service") await rabbitmq_client.connect() event_publisher = UnifiedEventPublisher(rabbitmq_client, "my-service") # Publish business events await event_publisher.publish_business_event( event_type=EVENT_TYPES.INVENTORY.STOCK_ADDED, tenant_id=tenant_id, data={"ingredient_id": "123", "quantity": 100.0} ) # Publish alerts (action required) await event_publisher.publish_alert( event_type="procurement.po_approval_needed", tenant_id=tenant_id, severity="high", # urgent, high, medium, low data={"po_id": "456", "supplier_name": "ABC Corp"} ) # Publish notifications (informational) await event_publisher.publish_notification( event_type="production.batch_completed", tenant_id=tenant_id, data={"batch_id": "789", "product_name": "Bread"} ) # Publish recommendations (suggestions) await event_publisher.publish_recommendation( event_type="forecasting.demand_surge_predicted", tenant_id=tenant_id, data={"product_name": "Croissants", "surge_percentage": 25.0} ) ``` ### 2. Event Types Constants Use predefined event types for consistency: ```python from shared.messaging import EVENT_TYPES # Inventory events EVENT_TYPES.INVENTORY.INGREDIENT_CREATED EVENT_TYPES.INVENTORY.STOCK_ADDED EVENT_TYPES.INVENTORY.LOW_STOCK_ALERT # Production events EVENT_TYPES.PRODUCTION.BATCH_CREATED EVENT_TYPES.PRODUCTION.BATCH_COMPLETED # Procurement events EVENT_TYPES.PROCUREMENT.PO_APPROVED EVENT_TYPES.PROCUREMENT.DELIVERY_SCHEDULED ``` ### 3. Service Integration Pattern #### In Service Main.py: ```python from shared.messaging import UnifiedEventPublisher, ServiceMessagingManager class MyService(StandardFastAPIService): def __init__(self): self.messaging_manager = None self.event_publisher = None # For alerts/notifications self.unified_publisher = None # For business events super().__init__( service_name="my-service", # ... other params enable_messaging=True ) async def _setup_messaging(self): try: self.messaging_manager = ServiceMessagingManager("my-service", settings.RABBITMQ_URL) success = await self.messaging_manager.setup() if success: self.event_publisher = self.messaging_manager.publisher self.unified_publisher = self.messaging_manager.publisher self.logger.info("Messaging setup completed") else: raise Exception("Failed to setup messaging") except Exception as e: self.logger.error("Messaging setup failed", error=str(e)) raise async def on_startup(self, app: FastAPI): await super().on_startup(app) # Pass publishers to services my_service = MyAlertService(self.event_publisher) my_event_service = MyEventService(self.unified_publisher) # Store in app state if needed app.state.my_service = my_service app.state.my_event_service = my_event_service async def on_shutdown(self, app: FastAPI): if self.messaging_manager: await self.messaging_manager.cleanup() await super().on_shutdown(app) ``` #### In Service Implementation: ```python from shared.messaging import UnifiedEventPublisher class MyEventService: def __init__(self, event_publisher: UnifiedEventPublisher): self.publisher = event_publisher async def handle_business_logic(self, tenant_id: UUID, data: Dict[str, Any]): # Publish business events await self.publisher.publish_business_event( event_type="mydomain.action_performed", tenant_id=tenant_id, data=data ) ``` ## Migration Guide ### Old Pattern (Deprecated): ```python # OLD - Don't use this anymore from shared.alerts.base_service import BaseAlertService class MyService(BaseAlertService): def __init__(self, config): super().__init__(config) async def send_alert(self, tenant_id, data): await self.publish_item(tenant_id, data, item_type="alert") ``` ### New Pattern (Recommended): ```python # NEW - Use UnifiedEventPublisher for all event types from shared.messaging import UnifiedEventPublisher class MyService: def __init__(self, event_publisher: UnifiedEventPublisher): self.publisher = event_publisher async def send_alert(self, tenant_id: UUID, data: Dict[str, Any]): await self.publisher.publish_alert( event_type="mydomain.alert_type", tenant_id=tenant_id, severity="high", data=data ) ``` ## Event Routing Events are routed using the following patterns: - **Alerts**: `alert.{domain}.{severity}` (e.g., `alert.inventory.high`) - **Notifications**: `notification.{domain}.info` (e.g., `notification.production.info`) - **Recommendations**: `recommendation.{domain}.medium` (e.g., `recommendation.forecasting.medium`) - **Business Events**: `business.{event_type}` (e.g., `business.inventory_stock_added`) ## Best Practices 1. **Consistent Naming**: Use lowercase, dot-separated event types (e.g., `inventory.stock.added`) 2. **Tenant Awareness**: Always include tenant_id for multi-tenant operations 3. **Data Minimization**: Include only essential data in events 4. **Error Handling**: Always wrap event publishing in try-catch blocks 5. **Service Names**: Use consistent service names matching your service definition 6. **Lifecycle Management**: Always clean up messaging resources during service shutdown