# Data Model Architecture --- title: 'Data Model Architecture' version: 1.5.0 status: first-draft owner: Nattanin Peancharoen last_updated: 2025-11-30 related: - specs/01-requirements/02-architecture.md - specs/01-requirements/03-functional-requirements.md - docs/4_Data_Dictionary_V1_4_5.md - docs/8_lcbp3_v1_4_5.sql --- ## 📋 Overview āđ€āļ­āļāļŠāļēāļĢāļ™āļĩāđ‰āļ­āļ˜āļīāļšāļēāļĒāļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāļ‚āļ­āļ‡ Data Model āļŠāļģāļŦāļĢāļąāļšāļĢāļ°āļšāļš LCBP3-DMS āđ‚āļ”āļĒāļ„āļĢāļ­āļšāļ„āļĨāļļāļĄāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ, āļ„āļ§āļēāļĄāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒāļĢāļ°āļŦāļ§āđˆāļēāļ‡āļ•āļēāļĢāļēāļ‡, āđāļĨāļ°āļŦāļĨāļąāļāļāļēāļĢāļ­āļ­āļāđāļšāļšāļ—āļĩāđˆāļŠāļģāļ„āļąāļ ## ðŸŽŊ Design Principles ### 1. Separation of Concerns - **Master-Revision Pattern**: āđāļĒāļāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ„āļĄāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ (Master) āļˆāļēāļāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāļĄāļĩāļāļēāļĢāđāļāđ‰āđ„āļ‚ (Revisions) - `correspondences` (Master) ↔ `correspondence_revisions` (Revisions) - `rfas` (Master) ↔ `rfa_revisions` (Revisions) - `shop_drawings` (Master) ↔ `shop_drawing_revisions` (Revisions) ### 2. Data Integrity - **Foreign Key Constraints**: āđƒāļŠāđ‰ FK āļ—āļļāļāļ„āļ§āļēāļĄāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒāđ€āļžāļ·āđˆāļ­āļĢāļąāļāļĐāļēāļ„āļ§āļēāļĄāļŠāļĄāļšāļđāļĢāļ“āđŒāļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ - **Soft Delete**: āđƒāļŠāđ‰ `deleted_at` āđāļ—āļ™āļāļēāļĢāļĨāļšāļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļĢāļīāļ‡ āđ€āļžāļ·āđˆāļ­āļĢāļąāļāļĐāļēāļ›āļĢāļ°āļ§āļąāļ•āļī - **Optimistic Locking**: āđƒāļŠāđ‰ `version` column āđƒāļ™ `document_number_counters` āļ›āđ‰āļ­āļ‡āļāļąāļ™ Race Condition ### 3. Flexibility & Extensibility - **JSON Details Field**: āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļ‰āļžāļēāļ°āļ›āļĢāļ°āđ€āļ āļ—āđƒāļ™ `correspondence_revisions.details` - **Virtual Columns**: āļŠāļĢāđ‰āļēāļ‡ Index āļˆāļēāļ JSON fields āļŠāļģāļŦāļĢāļąāļš Performance - **Master Data Tables**: āđāļĒāļāļ‚āđ‰āļ­āļĄāļđāļĨ Master (Types, Status, Codes) āđ€āļžāļ·āđˆāļ­āļ„āļ§āļēāļĄāļĒāļ·āļ”āļŦāļĒāļļāđˆāļ™ ### 4. Security & Audit - **RBAC (Role-Based Access Control)**: āļĢāļ°āļšāļšāļŠāļīāļ—āļ˜āļīāđŒāđāļšāļš Hierarchical Scope - **Audit Trail**: āļšāļąāļ™āļ—āļķāļāļœāļđāđ‰āļŠāļĢāđ‰āļēāļ‡/āđāļāđ‰āđ„āļ‚ āđāļĨāļ°āđ€āļ§āļĨāļēāđƒāļ™āļ—āļļāļāļ•āļēāļĢāļēāļ‡ - **Two-Phase File Upload**: āļ›āđ‰āļ­āļ‡āļāļąāļ™āđ„āļŸāļĨāđŒāļ‚āļĒāļ°āļ”āđ‰āļ§āļĒ Temporary Storage ## 🗂ïļ Database Schema Overview ### Entity Relationship Diagram ```mermaid erDiagram %% Core Entities organizations ||--o{ users : "employs" projects ||--o{ contracts : "contains" projects ||--o{ correspondences : "manages" %% RBAC users ||--o{ user_assignments : "has" roles ||--o{ user_assignments : "assigned_to" roles ||--o{ role_permissions : "has" permissions ||--o{ role_permissions : "granted_by" %% Correspondences correspondences ||--o{ correspondence_revisions : "has_revisions" correspondence_types ||--o{ correspondences : "categorizes" correspondence_status ||--o{ correspondence_revisions : "defines_state" disciplines ||--o{ correspondences : "classifies" %% RFAs rfas ||--o{ rfa_revisions : "has_revisions" rfa_types ||--o{ rfas : "categorizes" rfa_status_codes ||--o{ rfa_revisions : "defines_state" rfa_approve_codes ||--o{ rfa_revisions : "defines_result" disciplines ||--o{ rfas : "classifies" %% Drawings shop_drawings ||--o{ shop_drawing_revisions : "has_revisions" shop_drawing_main_categories ||--o{ shop_drawings : "categorizes" shop_drawing_sub_categories ||--o{ shop_drawings : "sub_categorizes" %% Attachments attachments ||--o{ correspondence_attachments : "attached_to" correspondences ||--o{ correspondence_attachments : "has" ``` ## 📊 Data Model Categories ### 1. ðŸĒ Core & Master Data #### 1.1 Organizations & Projects **Tables:** - `organization_roles` - āļšāļ—āļšāļēāļ—āļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ (OWNER, DESIGNER, CONSULTANT, CONTRACTOR) - `organizations` - āļ­āļ‡āļ„āđŒāļāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđƒāļ™āļĢāļ°āļšāļš - `projects` - āđ‚āļ„āļĢāļ‡āļāļēāļĢ - `contracts` - āļŠāļąāļāļāļēāļ āļēāļĒāđƒāļ•āđ‰āđ‚āļ„āļĢāļ‡āļāļēāļĢ - `project_organizations` - M:N āļĢāļ°āļŦāļ§āđˆāļēāļ‡ Projects āđāļĨāļ° Organizations - `contract_organizations` - M:N āļĢāļ°āļŦāļ§āđˆāļēāļ‡ Contracts āđāļĨāļ° Organizations āļžāļĢāđ‰āļ­āļĄ Role **Key Relationships:** ``` projects (1) ──→ (N) contracts projects (N) ←→ (N) organizations [via project_organizations] contracts (N) ←→ (N) organizations [via contract_organizations] ``` **Business Rules:** - Organization code āļ•āđ‰āļ­āļ‡āđ„āļĄāđˆāļ‹āđ‰āļģāļāļąāļ™āđƒāļ™āļĢāļ°āļšāļš - Contract āļ•āđ‰āļ­āļ‡āļœāļđāļāļāļąāļš Project āđ€āļŠāļĄāļ­ (ON DELETE CASCADE) - Soft delete āđƒāļŠāđ‰ `is_active` flag --- ### 2. ðŸ‘Ĩ Users & RBAC #### 2.1 User Management **Tables:** - `users` - āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™āļĢāļ°āļšāļš - `roles` - āļšāļ—āļšāļēāļ—āļžāļĢāđ‰āļ­āļĄ Scope (Global, Organization, Project, Contract) - `permissions` - āļŠāļīāļ—āļ˜āļīāđŒāļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ (49 permissions) - `role_permissions` - M:N mapping - `user_assignments` - āļāļēāļĢāļĄāļ­āļšāļŦāļĄāļēāļĒāļšāļ—āļšāļēāļ—āļžāļĢāđ‰āļ­āļĄ Scope Context **Scope Hierarchy:** ``` Global (āļ—āļąāđ‰āļ‡āļĢāļ°āļšāļš) ↓ Organization (āļĢāļ°āļ”āļąāļšāļ­āļ‡āļ„āđŒāļāļĢ) ↓ Project (āļĢāļ°āļ”āļąāļšāđ‚āļ„āļĢāļ‡āļāļēāļĢ) ↓ Contract (āļĢāļ°āļ”āļąāļšāļŠāļąāļāļāļē) ``` **Key Features:** - **Hierarchical Scope**: User āļŠāļēāļĄāļēāļĢāļ–āļĄāļĩāļŦāļĨāļēāļĒ Role āđƒāļ™āļŦāļĨāļēāļĒ Scope - **Scope Inheritance**: āļŠāļīāļ—āļ˜āļīāđŒāļĢāļ°āļ”āļąāļšāļšāļ™āļ„āļĢāļ­āļšāļ„āļĨāļļāļĄāļĢāļ°āļ”āļąāļšāļĨāđˆāļēāļ‡ - **Account Security**: Failed login tracking, Account locking, Password hashing (bcrypt) **Example User Assignment:** ```sql -- User A āđ€āļ›āđ‡āļ™ Editor āđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ TEAM INSERT INTO user_assignments (user_id, role_id, organization_id) VALUES (1, 4, 3); -- User B āđ€āļ›āđ‡āļ™ Project Manager āđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ LCBP3 INSERT INTO user_assignments (user_id, role_id, project_id) VALUES (2, 6, 1); ``` --- ### 3. ✉ïļ Correspondences (āđ€āļ­āļāļŠāļēāļĢāđ‚āļ•āđ‰āļ•āļ­āļš) #### 3.1 Master-Revision Pattern **Master Table: `correspondences`** āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ„āļĄāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡: - `correspondence_number` - āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (Unique per Project) - `correspondence_type_id` - āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ (RFA, RFI, TRANSMITTAL, etc.) - `discipline_id` - āļŠāļēāļ‚āļēāļ‡āļēāļ™ (GEN, STR, ARC, etc.) [NEW v1.4.5] - `project_id`, `originator_id` - āđ‚āļ„āļĢāļ‡āļāļēāļĢāđāļĨāļ°āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļŠāđˆāļ‡ **Revision Table: `correspondence_revisions`** āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡āđ„āļ”āđ‰: - `revision_number` - āļŦāļĄāļēāļĒāđ€āļĨāļ‚ Revision (0, 1, 2...) - `is_current` - Flag āļŠāļģāļŦāļĢāļąāļš Revision āļ›āļąāļˆāļˆāļļāļšāļąāļ™ (UNIQUE constraint) - `title`, `description` - āđ€āļ™āļ·āđ‰āļ­āļŦāļēāđ€āļ­āļāļŠāļēāļĢ - `correspondence_status_id` - āļŠāļ–āļēāļ™āļ° (DRAFT, SUBOWN, REPCSC, etc.) - `details` - JSON field āļŠāļģāļŦāļĢāļąāļšāļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļ‰āļžāļēāļ°āļ›āļĢāļ°āđ€āļ āļ— - Virtual Columns: `v_ref_project_id`, `v_ref_type`, `v_doc_subtype` (Indexed) **Supporting Tables:** - `correspondence_types` - Master āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ (10 types) - `correspondence_status` - Master āļŠāļ–āļēāļ™āļ° (23 status codes) - `correspondence_sub_types` - āļ›āļĢāļ°āđ€āļ āļ—āļĒāđˆāļ­āļĒāļŠāļģāļŦāļĢāļąāļš Document Numbering [NEW v1.4.5] - `disciplines` - āļŠāļēāļ‚āļēāļ‡āļēāļ™ (GEN, STR, ARC, etc.) [NEW v1.4.5] - `correspondence_recipients` - M:N āļœāļđāđ‰āļĢāļąāļš (TO/CC) - `correspondence_tags` - M:N Tags - `correspondence_references` - M:N Cross-references **Example Query - Get Current Revision:** ```sql SELECT c.correspondence_number, cr.title, cr.revision_label, cs.status_name FROM correspondences c JOIN correspondence_revisions cr ON c.id = cr.correspondence_id JOIN correspondence_status cs ON cr.correspondence_status_id = cs.id WHERE cr.is_current = TRUE AND c.deleted_at IS NULL; ``` --- ### 4. 📐 RFAs (Request for Approval) #### 4.1 RFA Structure **Master Table: `rfas`** - `rfa_type_id` - āļ›āļĢāļ°āđ€āļ āļ— RFA (DWG, DOC, MAT, SPC, etc.) - `discipline_id` - āļŠāļēāļ‚āļēāļ‡āļēāļ™ [NEW v1.4.5] **Revision Table: `rfa_revisions`** - `correspondence_id` - Link āļāļąāļš Correspondence (RFA āđ€āļ›āđ‡āļ™ Correspondence āļ›āļĢāļ°āđ€āļ āļ—āļŦāļ™āļķāđˆāļ‡) - `rfa_status_code_id` - āļŠāļ–āļēāļ™āļ° (DFT, FAP, FRE, FCO, ASB, OBS, CC) - `rfa_approve_code_id` - āļœāļĨāļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī (1A, 1C, 1N, 1R, 3C, 3R, 4X, 5N) - `approved_date` - āļ§āļąāļ™āļ—āļĩāđˆāļ­āļ™āļļāļĄāļąāļ•āļī **Supporting Tables:** - `rfa_types` - 11 āļ›āļĢāļ°āđ€āļ āļ— (Shop Drawing, Document, Material, etc.) - `rfa_status_codes` - 7 āļŠāļ–āļēāļ™āļ° - `rfa_approve_codes` - 8 āļĢāļŦāļąāļŠāļœāļĨāļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī - `rfa_items` - M:N āđ€āļŠāļ·āđˆāļ­āļĄ RFA (āļ›āļĢāļ°āđ€āļ āļ— DWG) āļāļąāļš Shop Drawing Revisions **RFA Workflow States:** ``` DFT (Draft) ↓ FAP (For Approve) / FRE (For Review) ↓ [Approval Process] ↓ FCO (For Construction) / ASB (As-Built) / 3R (Revise) / 4X (Reject) ``` --- ### 5. 📐 Drawings (āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡) #### 5.1 Contract Drawings (āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē) **Tables:** - `contract_drawing_volumes` - āđ€āļĨāđˆāļĄāđāļšāļš - `contract_drawing_cats` - āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ - `contract_drawing_sub_cats` - āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ - `contract_drawing_subcat_cat_maps` - M:N Mapping - `contract_drawings` - āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē **Hierarchy:** ``` Volume (āđ€āļĨāđˆāļĄ) └─ Category (āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ) └─ Sub-Category (āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ) └─ Drawing (āđāļšāļš) ``` #### 5.2 Shop Drawings (āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡) **Tables:** - `shop_drawing_main_categories` - āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ (ARCH, STR, MEP, etc.) - `shop_drawing_sub_categories` - āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ - `shop_drawings` - Master āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ - `shop_drawing_revisions` - Revisions - `shop_drawing_revision_contract_refs` - M:N āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē **Revision Tracking:** ```sql -- Get latest revision of a shop drawing SELECT sd.drawing_number, sdr.revision_label, sdr.revision_date FROM shop_drawings sd JOIN shop_drawing_revisions sdr ON sd.id = sdr.shop_drawing_id WHERE sd.drawing_number = 'SD-STR-001' ORDER BY sdr.revision_number DESC LIMIT 1; ``` --- ### 6. 🔄 Circulations & Transmittals #### 6.1 Circulations (āđƒāļšāđ€āļ§āļĩāļĒāļ™āļ āļēāļĒāđƒāļ™) **Tables:** - `circulation_status_codes` - āļŠāļ–āļēāļ™āļ° (OPEN, IN_REVIEW, COMPLETED, CANCELLED) - `circulations` - āđƒāļšāđ€āļ§āļĩāļĒāļ™ (1:1 āļāļąāļš Correspondence) **Workflow:** ``` OPEN → IN_REVIEW → COMPLETED ↓ CANCELLED ``` #### 6.2 Transmittals (āđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡) **Tables:** - `transmittals` - āļ‚āđ‰āļ­āļĄāļđāļĨ Transmittal (1:1 āļāļąāļš Correspondence) - `transmittal_items` - M:N āļĢāļēāļĒāļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļ™āļģāļŠāđˆāļ‡ **Purpose Types:** - FOR_APPROVAL - FOR_INFORMATION - FOR_REVIEW - OTHER --- ### 7. 📎 File Management #### 7.1 Two-Phase Storage Pattern **Table: `attachments`** **Phase 1: Temporary Upload** ```sql INSERT INTO attachments ( original_filename, stored_filename, file_path, mime_type, file_size, is_temporary, temp_id, uploaded_by_user_id, expires_at, checksum ) VALUES ( 'document.pdf', 'uuid-document.pdf', '/temp/uuid-document.pdf', 'application/pdf', 1024000, TRUE, 'temp-uuid-123', 1, NOW() + INTERVAL 1 HOUR, 'sha256-hash' ); ``` **Phase 2: Commit to Permanent** ```sql -- Update attachment to permanent UPDATE attachments SET is_temporary = FALSE, expires_at = NULL WHERE temp_id = 'temp-uuid-123'; -- Link to correspondence INSERT INTO correspondence_attachments (correspondence_id, attachment_id, is_main_document) VALUES (1, 123, TRUE); ``` **Junction Tables:** - `correspondence_attachments` - M:N - `circulation_attachments` - M:N - `shop_drawing_revision_attachments` - M:N (with file_type) - `contract_drawing_attachments` - M:N (with file_type) **Security Features:** - Checksum validation (SHA-256) - Automatic cleanup of expired temporary files - File type validation via `mime_type` --- ### 8. ðŸ”Ē Document Numbering #### 8.1 Format & Counter System **Tables:** - `document_number_formats` - Template āļĢāļđāļ›āđāļšāļšāđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ - `document_number_counters` - Running Number Counter with Optimistic Locking **Format Template Example:** ``` {ORG_CODE}-{TYPE_CODE}-{DISCIPLINE_CODE}-{YEAR}-{SEQ:4} → TEAM-RFA-STR-2025-0001 ``` **Counter Table Structure:** ```sql CREATE TABLE document_number_counters ( project_id INT, originator_organization_id INT, correspondence_type_id INT, discipline_id INT DEFAULT 0, -- NEW v1.4.5 current_year INT, version INT DEFAULT 0, -- Optimistic Lock last_number INT DEFAULT 0, PRIMARY KEY (project_id, originator_organization_id, correspondence_type_id, discipline_id, current_year) ); ``` **Optimistic Locking Pattern:** ```sql -- Get next number with version check UPDATE document_number_counters SET last_number = last_number + 1, version = version + 1 WHERE project_id = 1 AND originator_organization_id = 3 AND correspondence_type_id = 1 AND discipline_id = 2 AND current_year = 2025 AND version = @current_version; -- Optimistic lock check -- If affected rows = 0, retry (conflict detected) ``` --- ## 🔐 Security & Audit ### 1. Audit Logging **Table: `audit_logs`** āļšāļąāļ™āļ—āļķāļāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡āļŠāļģāļ„āļąāļ: - User actions (CREATE, UPDATE, DELETE) - Entity type āđāļĨāļ° Entity ID - Old/New values (JSON) - IP Address, User Agent ### 2. User Preferences **Table: `user_preferences`** āđ€āļāđ‡āļšāļāļēāļĢāļ•āļąāđ‰āļ‡āļ„āđˆāļēāļŠāđˆāļ§āļ™āļ•āļąāļ§: - Language preference - Notification settings - UI preferences (JSON) ### 3. JSON Schema Validation **Table: `json_schemas`** āđ€āļāđ‡āļš Schema āļŠāļģāļŦāļĢāļąāļš Validate JSON fields: - `correspondence_revisions.details` - `user_preferences.preferences` --- ## 📈 Performance Optimization ### 1. Indexing Strategy **Primary Indexes:** - Primary Keys (AUTO_INCREMENT) - Foreign Keys (automatic in InnoDB) - Unique Constraints (business keys) **Secondary Indexes:** ```sql -- Correspondence search CREATE INDEX idx_corr_type_status ON correspondence_revisions(correspondence_type_id, correspondence_status_id); CREATE INDEX idx_corr_date ON correspondence_revisions(document_date); -- Virtual columns for JSON CREATE INDEX idx_v_ref_project ON correspondence_revisions(v_ref_project_id); CREATE INDEX idx_v_doc_subtype ON correspondence_revisions(v_doc_subtype); -- User lookup CREATE INDEX idx_user_email ON users(email); CREATE INDEX idx_user_org ON users(primary_organization_id, is_active); ``` ### 2. Virtual Columns āđƒāļŠāđ‰ Virtual Columns āļŠāļģāļŦāļĢāļąāļš Index JSON fields: ```sql ALTER TABLE correspondence_revisions ADD COLUMN v_ref_project_id INT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '$.ref_project_id'))) VIRTUAL, ADD INDEX idx_v_ref_project(v_ref_project_id); ``` ### 3. Partitioning (Future) āļžāļīāļˆāļēāļĢāļ“āļē Partition āļ•āļēāļĢāļēāļ‡ `audit_logs` āļ•āļēāļĄāļ›āļĩ: ```sql ALTER TABLE audit_logs PARTITION BY RANGE (YEAR(created_at)) ( PARTITION p2024 VALUES LESS THAN (2025), PARTITION p2025 VALUES LESS THAN (2026), PARTITION p_future VALUES LESS THAN MAXVALUE ); ``` --- ## 🔄 Migration Strategy ### 1. TypeORM Migrations āđƒāļŠāđ‰ TypeORM Migration āļŠāļģāļŦāļĢāļąāļš Schema Changes: ```typescript // File: backend/src/migrations/1234567890-AddDisciplineToCorrespondences.ts import { MigrationInterface, QueryRunner } from 'typeorm'; export class AddDisciplineToCorrespondences1234567890 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(` ALTER TABLE correspondences ADD COLUMN discipline_id INT NULL COMMENT 'āļŠāļēāļ‚āļēāļ‡āļēāļ™ (āļ–āđ‰āļēāļĄāļĩ)' AFTER correspondence_type_id `); await queryRunner.query(` ALTER TABLE correspondences ADD CONSTRAINT fk_corr_discipline FOREIGN KEY (discipline_id) REFERENCES disciplines(id) ON DELETE SET NULL `); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query( `ALTER TABLE correspondences DROP FOREIGN KEY fk_corr_discipline` ); await queryRunner.query( `ALTER TABLE correspondences DROP COLUMN discipline_id` ); } } ``` ### 2. Data Seeding āđƒāļŠāđ‰ Seed Scripts āļŠāļģāļŦāļĢāļąāļš Master Data: ```typescript // File: backend/src/seeds/1-organizations.seed.ts export class OrganizationSeeder implements Seeder { public async run(dataSource: DataSource): Promise { const repository = dataSource.getRepository(Organization); await repository.save([ { organization_code: 'āļāļ—āļ—.', organization_name: 'Port Authority of Thailand', }, { organization_code: 'TEAM', organization_name: 'TEAM Consulting Engineering', }, // ... ]); } } ``` --- ## 📚 Best Practices ### 1. Naming Conventions - **Tables**: `snake_case`, plural (e.g., `correspondences`, `users`) - **Columns**: `snake_case` (e.g., `correspondence_number`, `created_at`) - **Foreign Keys**: `{referenced_table_singular}_id` (e.g., `project_id`, `user_id`) - **Junction Tables**: `{table1}_{table2}` (e.g., `correspondence_tags`) ### 2. Timestamp Columns āļ—āļļāļāļ•āļēāļĢāļēāļ‡āļ„āļ§āļĢāļĄāļĩ: - `created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP` - `updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP` ### 3. Soft Delete āđƒāļŠāđ‰ `deleted_at DATETIME NULL` āđāļ—āļ™āļāļēāļĢāļĨāļšāļˆāļĢāļīāļ‡: ```sql -- Soft delete UPDATE correspondences SET deleted_at = NOW() WHERE id = 1; -- Query active records SELECT * FROM correspondences WHERE deleted_at IS NULL; ``` ### 4. JSON Field Guidelines - āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡ Query āļšāđˆāļ­āļĒ - āļŠāļĢāđ‰āļēāļ‡ Virtual Columns āļŠāļģāļŦāļĢāļąāļš fields āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡ Index - Validate āļ”āđ‰āļ§āļĒ JSON Schema - Document structure āđƒāļ™ Data Dictionary --- ## 🔗 Related Documentation - [System Architecture](../01-requirements/01-02-architecture.md) - āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāļĢāļ°āļšāļšāđ‚āļ”āļĒāļĢāļ§āļĄ - [API Design](02-02-api-design.md) - āļāļēāļĢāļ­āļ­āļāđāļšāļš API - [Data Dictionary v1.4.5](../../docs/4_Data_Dictionary_V1_4_5.md) - āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āļ•āļēāļĢāļēāļ‡āļ—āļąāđ‰āļ‡āļŦāļĄāļ” - [SQL Schema v1.4.5](../../docs/8_lcbp3_v1_4_5.sql) - SQL Script āļŠāļģāļŦāļĢāļąāļšāļŠāļĢāđ‰āļēāļ‡āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ - [Functional Requirements](../01-requirements/01-03-functional-requirements.md) - āļ„āļ§āļēāļĄāļ•āđ‰āļ­āļ‡āļāļēāļĢāļ”āđ‰āļēāļ™āļŸāļąāļ‡āļāđŒāļŠāļąāļ™ --- ## 📝 Version History | Version | Date | Author | Changes | | ------- | ---------- | -------------------- | ---------------------------------------------- | | 1.5.0 | 2025-11-30 | Nattanin Peancharoen | Initial data model documentation | | 1.4.5 | 2025-11-29 | System | Added disciplines and correspondence_sub_types |