"""Notification provider interface. Every alert dispatch (app/services/ alert_service.py) and every "test destination" action goes through this Protocol, never a specific vendor SDK - swapping providers or adding a new one doesn't touch the dispatch logic. """ from __future__ import annotations from dataclasses import dataclass from typing import Protocol @dataclass(frozen=True) class NotificationMessage: destination_value: str subject: str body_text: str body_html: str | None = None @dataclass(frozen=True) class DeliveryResult: success: bool external_message_id: str | None = None error: str | None = None class NotificationProvider(Protocol): provider_name: str async def send(self, message: NotificationMessage) -> DeliveryResult: ...