# Architect — F-024 Notifications: transactional email

## Feature
F-024 sends transactional emails driven by domain events with idempotency per event id and a swappable provider.

## Design

### Module boundaries
Create `project/src/modules/notifications/` with domain/application/infrastructure/api/tests. Notifications owns message persistence and provider dispatch; domain code uses only the `EmailProvider` interface.

### Data model
Add migration `018_notifications.js`:
- `notifications_messages`: id, event_id text unique, channel text default 'email', template text, recipient text, subject text, body text, status text default 'sent', created_at timestamptz.
- UNIQUE `event_id` for idempotency.

### Templates
- `order_confirmation`, `payment_failed`, `order_shipped` mapped to events `OrderPaid`, `PaymentFailed`, `OrderShipped`.

### API
- Internal `/notifications/dispatch` (admin/test) to trigger a dispatch by template and recipient.
- Internal `/notifications/messages/:eventId` to inspect dispatch status.

## Acceptance trace
- PaymentSucceeded -> order_confirmation email queued, idempotent on event_id.
- Replay same event -> no duplicate row.
- Provider swap touches only infrastructure.
