Files
orion/app/modules/messaging/docs/data-model.md
Samir Boulahtit f141cc4e6a docs: migrate module documentation to single source of truth
Move 39 documentation files from top-level docs/ into each module's
docs/ folder, accessible via symlinks from docs/modules/. Create
data-model.md files for 10 modules with full schema documentation.
Replace originals with redirect stubs. Remove empty guide stubs.

Modules migrated: tenancy, billing, loyalty, marketplace, orders,
messaging, cms, catalog, inventory, hosting, prospecting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 23:38:37 +01:00

291 lines
13 KiB
Markdown

# Messaging Data Model
Entity relationships and database schema for the messaging module.
## Entity Relationship Overview
```
Store 1──1 StoreEmailSettings
Store 1──* StoreEmailTemplate
Store 1──* Conversation 1──* Message 1──* MessageAttachment
└──* ConversationParticipant
EmailTemplate 1──* EmailLog
```
## Models
### EmailTemplate
Multi-language email templates stored in database with Jinja2 variable interpolation.
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| `id` | Integer | PK | Primary key |
| `code` | String(100) | not null, indexed | Template identifier (e.g., "signup_welcome") |
| `language` | String(5) | not null, default "en" | Language code |
| `name` | String(255) | not null | Human-readable name |
| `description` | Text | nullable | Template purpose description |
| `category` | String(50) | not null, default "system", indexed | auth, orders, billing, system, marketing |
| `subject` | String(500) | not null | Subject line (supports variables) |
| `body_html` | Text | not null | HTML body content |
| `body_text` | Text | nullable | Plain text fallback |
| `variables` | Text | nullable | JSON list of expected variables |
| `required_variables` | Text | nullable | JSON list of mandatory variables |
| `is_active` | Boolean | not null, default True | Activation status |
| `is_platform_only` | Boolean | not null, default False | If True, stores cannot override |
| `created_at` | DateTime | tz-aware | Record creation time |
| `updated_at` | DateTime | tz-aware | Record update time |
**Unique Index**: `(code, language)`
### StoreEmailTemplate
Store-specific email template overrides. Stores can customize platform templates without modifying defaults.
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| `id` | Integer | PK | Primary key |
| `store_id` | Integer | FK, not null, indexed | Store owning the override |
| `template_code` | String(100) | not null, indexed | References EmailTemplate.code |
| `language` | String(5) | not null, default "en" | Language code |
| `name` | String(255) | nullable | Custom name (null = use platform) |
| `subject` | String(500) | not null | Custom subject line |
| `body_html` | Text | not null | Custom HTML body |
| `body_text` | Text | nullable | Custom plain text body |
| `is_active` | Boolean | not null, default True | Activation status |
| `created_at` | DateTime | tz-aware | Record creation time |
| `updated_at` | DateTime | tz-aware | Record update time |
**Unique Constraint**: `(store_id, template_code, language)`
### EmailLog
Email sending history and tracking for debugging, analytics, and compliance.
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| `id` | Integer | PK | Primary key |
| `template_code` | String(100) | nullable, indexed | Reference to template code |
| `template_id` | Integer | FK, nullable | Reference to template |
| `recipient_email` | String(255) | not null, indexed | Recipient address |
| `recipient_name` | String(255) | nullable | Recipient name |
| `subject` | String(500) | not null | Email subject line |
| `body_html` | Text | nullable | HTML body snapshot |
| `body_text` | Text | nullable | Plain text body snapshot |
| `from_email` | String(255) | not null | Sender email address |
| `from_name` | String(255) | nullable | Sender name |
| `reply_to` | String(255) | nullable | Reply-to address |
| `status` | String(20) | not null, default "pending", indexed | pending, sent, failed, bounced, delivered, opened, clicked |
| `sent_at` | DateTime | nullable | When sent |
| `delivered_at` | DateTime | nullable | When delivered |
| `opened_at` | DateTime | nullable | When opened |
| `clicked_at` | DateTime | nullable | When clicked |
| `error_message` | Text | nullable | Error details if failed |
| `retry_count` | Integer | not null, default 0 | Retry attempts |
| `provider` | String(50) | nullable | smtp, sendgrid, mailgun, ses |
| `provider_message_id` | String(255) | nullable, indexed | Provider's message ID |
| `store_id` | Integer | FK, nullable, indexed | Associated store |
| `user_id` | Integer | FK, nullable, indexed | Associated user |
| `related_type` | String(50) | nullable | Related entity type |
| `related_id` | Integer | nullable | Related entity ID |
| `extra_data` | Text | nullable | JSON additional context |
| `created_at` | DateTime | tz-aware | Record creation time |
| `updated_at` | DateTime | tz-aware | Record update time |
### StoreEmailSettings
Per-store email sending configuration. One-to-one with Store.
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| `id` | Integer | PK | Primary key |
| `store_id` | Integer | FK, unique, not null | One-to-one with store |
| `from_email` | String(255) | not null | Sender email address |
| `from_name` | String(100) | not null | Sender display name |
| `reply_to_email` | String(255) | nullable | Reply-to address |
| `signature_text` | Text | nullable | Plain text signature |
| `signature_html` | Text | nullable | HTML signature/footer |
| `provider` | String(20) | not null, default "smtp" | smtp, sendgrid, mailgun, ses |
| `smtp_host` | String(255) | nullable | SMTP server hostname |
| `smtp_port` | Integer | nullable, default 587 | SMTP port |
| `smtp_username` | String(255) | nullable | SMTP username |
| `smtp_password` | String(500) | nullable | SMTP password (encrypted) |
| `smtp_use_tls` | Boolean | not null, default True | Use TLS |
| `smtp_use_ssl` | Boolean | not null, default False | Use SSL (port 465) |
| `sendgrid_api_key` | String(500) | nullable | SendGrid API key (encrypted) |
| `mailgun_api_key` | String(500) | nullable | Mailgun API key (encrypted) |
| `mailgun_domain` | String(255) | nullable | Mailgun domain |
| `ses_access_key_id` | String(100) | nullable | SES access key ID |
| `ses_secret_access_key` | String(500) | nullable | SES secret key (encrypted) |
| `ses_region` | String(50) | nullable, default "eu-west-1" | AWS region |
| `is_configured` | Boolean | not null, default False | Has complete config |
| `is_verified` | Boolean | not null, default False | Test email succeeded |
| `last_verified_at` | DateTime | nullable, tz-aware | Last verification |
| `verification_error` | Text | nullable | Last error message |
| `created_at` | DateTime | tz-aware | Record creation time |
| `updated_at` | DateTime | tz-aware | Record update time |
**Index**: `(store_id, is_configured)`
### AdminNotification
Admin-specific notifications for system alerts and warnings.
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| `id` | Integer | PK | Primary key |
| `type` | String(50) | not null, indexed | system_alert, store_issue, import_failure |
| `priority` | String(20) | not null, default "normal", indexed | low, normal, high, critical |
| `title` | String(200) | not null | Notification title |
| `message` | Text | not null | Notification message |
| `is_read` | Boolean | not null, default False, indexed | Read status |
| `read_at` | DateTime | nullable | When read |
| `read_by_user_id` | Integer | FK, nullable | User who read it |
| `action_required` | Boolean | not null, default False, indexed | Action needed |
| `action_url` | String(500) | nullable | Link to relevant admin page |
| `notification_metadata` | JSON | nullable | Additional context |
| `created_at` | DateTime | tz-aware | Record creation time |
| `updated_at` | DateTime | tz-aware | Record update time |
### Conversation
Threaded conversation between participants with multi-tenant isolation.
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| `id` | Integer | PK | Primary key |
| `conversation_type` | Enum | not null, indexed | admin_store, store_customer, admin_customer |
| `subject` | String(500) | not null | Thread subject line |
| `store_id` | Integer | FK, nullable, indexed | Multi-tenant isolation |
| `is_closed` | Boolean | not null, default False | Closed status |
| `closed_at` | DateTime | nullable | When closed |
| `closed_by_type` | Enum | nullable | Type of closer |
| `closed_by_id` | Integer | nullable | ID of closer |
| `last_message_at` | DateTime | nullable, indexed | Last activity |
| `message_count` | Integer | not null, default 0 | Total messages |
| `created_at` | DateTime | tz-aware | Record creation time |
| `updated_at` | DateTime | tz-aware | Record update time |
**Composite Index**: `(conversation_type, store_id)`
### ConversationParticipant
Links participants (users or customers) to conversations with polymorphic relationships.
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| `id` | Integer | PK | Primary key |
| `conversation_id` | Integer | FK, not null, indexed | Parent conversation |
| `participant_type` | Enum | not null | admin, store, customer |
| `participant_id` | Integer | not null, indexed | Polymorphic participant ID |
| `store_id` | Integer | FK, nullable | Store context |
| `unread_count` | Integer | not null, default 0 | Unread messages |
| `last_read_at` | DateTime | nullable | Last read time |
| `email_notifications` | Boolean | not null, default True | Email notification pref |
| `muted` | Boolean | not null, default False | Muted status |
| `created_at` | DateTime | tz-aware | Record creation time |
| `updated_at` | DateTime | tz-aware | Record update time |
**Unique Constraint**: `(conversation_id, participant_type, participant_id)`
**Composite Index**: `(participant_type, participant_id)`
### Message
Individual message within a conversation thread.
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| `id` | Integer | PK | Primary key |
| `conversation_id` | Integer | FK, not null, indexed | Parent conversation |
| `sender_type` | Enum | not null | admin, store, customer |
| `sender_id` | Integer | not null, indexed | Polymorphic sender ID |
| `content` | Text | not null | Message body |
| `is_system_message` | Boolean | not null, default False | System-generated flag |
| `is_deleted` | Boolean | not null, default False | Soft delete flag |
| `deleted_at` | DateTime | nullable | When deleted |
| `deleted_by_type` | Enum | nullable | Type of deleter |
| `deleted_by_id` | Integer | nullable | ID of deleter |
| `created_at` | DateTime | tz-aware | Record creation time |
| `updated_at` | DateTime | tz-aware | Record update time |
**Composite Index**: `(conversation_id, created_at)`
### MessageAttachment
File attachments for messages.
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| `id` | Integer | PK | Primary key |
| `message_id` | Integer | FK, not null, indexed | Parent message |
| `filename` | String(255) | not null | System filename |
| `original_filename` | String(255) | not null | Original upload name |
| `file_path` | String(1000) | not null | Storage path |
| `file_size` | Integer | not null | File size in bytes |
| `mime_type` | String(100) | not null | MIME type |
| `is_image` | Boolean | not null, default False | Image flag |
| `image_width` | Integer | nullable | Width in pixels |
| `image_height` | Integer | nullable | Height in pixels |
| `thumbnail_path` | String(1000) | nullable | Thumbnail path |
| `created_at` | DateTime | tz-aware | Record creation time |
| `updated_at` | DateTime | tz-aware | Record update time |
## Enums
### EmailCategory
| Value | Description |
|-------|-------------|
| `auth` | Signup, password reset, verification |
| `orders` | Order confirmations, shipping |
| `billing` | Invoices, payment failures |
| `system` | Team invites, notifications |
| `marketing` | Newsletters, promotions |
### EmailStatus
| Value | Description |
|-------|-------------|
| `pending` | Queued for sending |
| `sent` | Sent to provider |
| `failed` | Send failed |
| `bounced` | Bounced back |
| `delivered` | Confirmed delivered |
| `opened` | Recipient opened |
| `clicked` | Link clicked |
### EmailProvider
| Value | Description |
|-------|-------------|
| `smtp` | Standard SMTP (all tiers) |
| `sendgrid` | SendGrid API (Business+ tier) |
| `mailgun` | Mailgun API (Business+ tier) |
| `ses` | Amazon SES (Business+ tier) |
### ConversationType
| Value | Description |
|-------|-------------|
| `admin_store` | Admin-store conversations |
| `store_customer` | Store-customer conversations |
| `admin_customer` | Admin-customer conversations |
### ParticipantType
| Value | Description |
|-------|-------------|
| `admin` | Platform admin user |
| `store` | Store team user |
| `customer` | Customer |
## Design Patterns
- **Template override system**: Platform templates + per-store overrides with language fallback
- **Polymorphic participants**: Conversations support admin, store, and customer participants
- **Email tracking**: Full lifecycle tracking (sent → delivered → opened → clicked)
- **Provider abstraction**: Multiple email providers with per-store configuration
- **Premium tier gating**: SendGrid, Mailgun, SES require Business+ tier
- **Soft deletes**: Messages support soft delete with audit trail