# 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