BMAD-METHOD/CURRENT-V2/docs/data-models.md

5.0 KiB

Data Models: WasaPrecruit MVP

This document describes the core data entities and their structures stored in the database (PostgreSQL) for the WasaPrecruit MVP.

Notation:

  • Types follow TypeScript syntax.
  • PK: Primary Key
  • FK: Foreign Key
  • ?: Optional field

1. Aspirant

Represents a potential modeling aspirant who has interacted with the system via WhatsApp and potentially submitted the web form.

interface Aspirant {
  id: string; // PK - Generated UUID or unique identifier
  whatsappId: string; // Unique - Aspirant's WhatsApp phone number (e.g., E.164 format)
  whatsappName?: string; // Name provided by WhatsApp profile (can be unreliable)
  
  // Data from Web Form
  firstName?: string;
  lastName?: string;
  email?: string; // Optional, depending on form
  location?: string; // e.g., City/Region
  preferredModelingType?: string[]; // Array of strings (e.g., ['Runway', 'Commercial'])
  hasKids?: boolean;
  preferredShift?: 'morning' | 'afternoon' | 'evening' | 'flexible' | 'any';
  unavailableShifts?: ('morning' | 'afternoon' | 'evening')[];
  notes?: string; // Any additional notes from the form
  formSubmittedAt?: Date;

  // System Data
  photoUrl?: string; // URL to the photo stored in S3 (submitted via WhatsApp)
  conversationId?: string; // FK to Conversation (might be implicitly linked via whatsappId)
  createdAt: Date;
  updatedAt: Date;
}

Indexes:

  • whatsappId (Unique)
  • createdAt
  • formSubmittedAt

2. Conversation

Represents a chat conversation between the system/recruiter and an aspirant.

interface Conversation {
  id: string; // PK - Generated UUID
  aspirantWhatsappId: string; // FK (conceptually) to Aspirant via whatsappId - identifies the participant
  // OR alternatively: aspirantId: string; // FK to Aspirant.id if preferred
  
  status: 'new' | 'open' | 'pending_form' | 'pending_photo' | 'needs_attention' | 'closed' | 'archived'; // Conversation status
  assignedRecruiterId?: string; // FK to Recruiter/User (if assignment is implemented later)
  lastMessageTimestamp?: Date; // Timestamp of the last message for sorting
  unreadCount: number; // For recruiter view

  createdAt: Date;
  updatedAt: Date;
}

Indexes:

  • aspirantWhatsappId
  • status
  • lastMessageTimestamp
  • assignedRecruiterId (if used)

Note: A conversation might be implicitly defined by messages associated with a unique aspirantWhatsappId. A separate Conversation table is useful for managing state (status, assignedRecruiterId, unreadCount) independent of individual messages.

3. Message

Represents a single message within a conversation.

interface Message {
  id: string; // PK - Generated UUID or provider's message ID (if unique and suitable)
  conversationId: string; // FK to Conversation
  senderType: 'aspirant' | 'recruiter' | 'bot';
  senderId?: string; // FK to Recruiter/User ID if senderType is 'recruiter'
  aspirantWhatsappId: string; // Denormalized for easier querying by aspirant
  
  text?: string; // Message content (if text)
  mediaUrl?: string; // URL to media (e.g., photo in S3)
  mediaType?: 'image' | 'video' | 'audio' | 'document'; // Type of media
  
  providerMessageId?: string; // Original message ID from WhatsApp provider (e.g., Twilio SID)
  status: 'sent' | 'delivered' | 'read' | 'failed'; // Status of outbound messages
  timestamp: Date; // Time the message was sent/received
  
  createdAt: Date;
}

Indexes:

  • conversationId
  • aspirantWhatsappId
  • timestamp
  • senderType

4. Recruiter (User Model)

Represents an agency recruiter using the platform (details depend on authentication provider like Cognito).

interface Recruiter {
  id: string; // PK - Often corresponds to Cognito User Sub
  name: string;
  email: string; // Unique
  // Add roles/permissions if needed later
  createdAt: Date;
  updatedAt: Date;
}

Indexes:

  • email (Unique)

Relationships

  • One Aspirant potentially relates to one Conversation (based on whatsappId).
  • One Conversation contains many Messages.
  • One Recruiter can send many Messages.

Notes on Evolution

  • Normalization: Some fields like aspirantWhatsappId are denormalized onto Message for query performance. Assess trade-offs if data consistency becomes complex.
  • JSONB: Consider using PostgreSQL's JSONB type for flexible fields like formDetails if the web form structure is expected to change frequently, though explicit columns offer better indexing and type safety initially.
  • Scalability: Partitioning message tables by date or conversationId might be necessary for very high volumes in the future.

Change Log

Change Date Version Description Author
Initial draft YYYY-MM-DD 0.1 Initial draft {Agent/Person}
... ... ... ... ...