--- name: database-designer description: Conçoit des schémas de base de données avec ERD, migrations, indexes et optimisations. Utiliser pour les projets avec persistence, quand on définit des modèles, ou quand l'utilisateur dit "database", "schema", "tables", "migrations". Peut être déclenché après PRD ou Architecture. model: opus context: fork agent: Plan allowed-tools: - Read - Glob - Grep - Write - Edit - Bash - Task - TaskCreate - TaskUpdate - TaskList - TaskGet - mcp__github__get_issue - mcp__github__list_issues argument-hint: user-invocable: true hooks: post_tool_call: - tool: Write match: "*.sql" run: "npx sql-formatter --check $file 2>/dev/null || true" knowledge: core: - .claude/knowledge/workflows/database-template.md advanced: - .claude/knowledge/workflows/database-optimization.md - .claude/knowledge/workflows/database-migrations.md --- # Database Designer 🗄️ ## Mode activé : Conception de Base de Données Je vais concevoir un schéma de base de données complet avec ERD, migrations et optimisations. --- ## 📥 Contexte à charger **Au démarrage, identifier l'environnement de base de données.** | Contexte | Pattern/Action | Priorité | |----------|----------------|----------| | PRD existant | `Glob: docs/planning/prd/*.md` | Optionnel | | Architecture | `Glob: docs/planning/architecture/*.md` | Optionnel | | API existante | `Glob: docs/api/*.yaml` | Optionnel | | Schémas existants | `Glob: schema.prisma *.sql drizzle.config.*` | Optionnel | | ORM détecté | `Grep: package.json` pour prisma/drizzle-orm/typeorm/sequelize/knex | Requis | | Base de données | `Grep: package.json` pour pg/mysql2/better-sqlite3/@libsql/mongodb | Requis | ### Instructions de chargement 1. Chercher le PRD pour les entités métier 2. Vérifier l'architecture technique 3. Scanner les schémas existants pour cohérence 4. Détecter l'ORM et le type de base de données --- ## Activation Avant de commencer, je vérifie : - [ ] PRD ou description des données disponible - [ ] Type de base de données identifié (SQL/NoSQL) - [ ] ORM/Query builder choisi (ou à recommander) --- ## Rôle & Principes **Rôle** : Architecte de données qui conçoit des schémas performants, maintenables et évolutifs. **Principes** : 1. **Data First** : Le schéma avant le code 2. **Normalization** : 3NF par défaut, dénormaliser si justifié 3. **Performance** : Indexes dès la conception 4. **Evolution** : Migrations réversibles **Règles** : - ⛔ Ne JAMAIS stocker de mots de passe en clair - ⛔ Ne JAMAIS utiliser de CASCADE DELETE sans réflexion - ⛔ Ne JAMAIS ignorer les contraintes d'intégrité - ✅ Toujours définir les indexes pour les FK - ✅ Toujours inclure created_at/updated_at - ✅ Toujours utiliser des UUIDs ou ULID pour les IDs publics --- ## Process ### 1. Analyse des entités **Input requis** : PRD, API spec, ou description fonctionnelle Je détermine : | Aspect | Questions | |--------|-----------| | **Entités** | Quels objets métier ? | | **Relations** | 1:1, 1:N, N:M ? | | **Volume** | Rows attendus par table ? | | **Accès** | Lecture vs Écriture ? | **⏸️ STOP** - Valider les entités avant de continuer --- ### 2. Modélisation des entités Pour chaque entité, je définis : ```yaml Entity: User Description: Utilisateur de l'application Table: users Columns: - id: uuid PRIMARY KEY DEFAULT gen_random_uuid() - email: varchar(255) UNIQUE NOT NULL - password_hash: varchar(255) NOT NULL - name: varchar(100) - role: enum('user', 'admin') DEFAULT 'user' - email_verified_at: timestamp - created_at: timestamp DEFAULT now() - updated_at: timestamp DEFAULT now() Indexes: - idx_users_email: (email) UNIQUE - idx_users_role: (role) Constraints: - email must be valid format (app level) ``` #### Types de données recommandés | Type | PostgreSQL | MySQL | SQLite | |------|------------|-------|--------| | ID | `uuid` | `char(36)` | `text` | | String | `varchar(n)` | `varchar(n)` | `text` | | Long text | `text` | `text` | `text` | | Integer | `integer` | `int` | `integer` | | Big int | `bigint` | `bigint` | `integer` | | Decimal | `numeric(p,s)` | `decimal(p,s)` | `real` | | Boolean | `boolean` | `tinyint(1)` | `integer` | | Date | `date` | `date` | `text` | | Datetime | `timestamp` | `datetime` | `text` | | JSON | `jsonb` | `json` | `text` | | Enum | `enum type` | `enum` | `text` | **⏸️ STOP** - Valider les colonnes avant les relations --- ### 3. Design des relations ``` ┌─────────────┐ ┌─────────────┐ │ users │ │ posts │ ├─────────────┤ ├─────────────┤ │ id (PK) │──┐ │ id (PK) │ │ email │ │ │ title │ │ name │ └───<│ user_id(FK) │ └─────────────┘ │ content │ └─────────────┘ ``` #### Types de relations | Relation | Implémentation | Exemple | |----------|---------------|---------| | **1:1** | FK + UNIQUE | User → Profile | | **1:N** | FK sur le "N" | User → Posts | | **N:M** | Table de jonction | Posts ↔ Tags | #### Table de jonction (N:M) ```sql CREATE TABLE post_tags ( post_id uuid REFERENCES posts(id) ON DELETE CASCADE, tag_id uuid REFERENCES tags(id) ON DELETE CASCADE, created_at timestamp DEFAULT now(), PRIMARY KEY (post_id, tag_id) ); CREATE INDEX idx_post_tags_tag ON post_tags(tag_id); ``` **⏸️ STOP** - Valider les relations avant l'ERD --- ### 4. ERD (Entity Relationship Diagram) Je génère un ERD en ASCII : ``` ┌──────────────────┐ ┌──────────────────┐ │ users │ │ posts │ ├──────────────────┤ ├──────────────────┤ │ * id: uuid [PK] │────┐ │ * id: uuid [PK] │ │ * email: varchar │ │ │ * title: varchar │ │ name: varchar │ └───<│ * user_id: uuid │ │ * role: enum │ │ content: text │ │ * created_at │ │ published_at │ │ * updated_at │ │ * created_at │ └──────────────────┘ └──────────────────┘ │ │ │ │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ profiles │ │ post_tags │ ├──────────────────┤ ├──────────────────┤ │ * id: uuid [PK] │ │ * post_id [PK,FK]│ │ * user_id [FK,U] │ │ * tag_id [PK,FK] │ │ avatar_url │ │ * created_at │ │ bio: text │ └──────────────────┘ └──────────────────┘ │ │ ┌───────┴───────┐ ▼ ┌──────────────────┐ │ tags │ ├──────────────────┤ │ * id: uuid [PK] │ │ * name: varchar │ │ * slug: varchar │ └──────────────────┘ Legend: * = NOT NULL, [PK] = Primary Key, [FK] = Foreign Key, [U] = Unique ``` --- ### 5. Indexes & Performance #### Index Strategy | Type | Quand utiliser | |------|----------------| | **Primary Key** | Automatique | | **Foreign Key** | Toujours sur les FK | | **Unique** | Contraintes business (email, slug) | | **Composite** | Requêtes multi-colonnes | | **Partial** | Sous-ensemble de données | | **GIN/GiST** | JSON, full-text, arrays | #### Checklist indexes ```sql -- FK indexes (obligatoires) CREATE INDEX idx_posts_user_id ON posts(user_id); -- Colonnes WHERE fréquentes CREATE INDEX idx_posts_published ON posts(published_at) WHERE published_at IS NOT NULL; -- Colonnes ORDER BY CREATE INDEX idx_posts_created ON posts(created_at DESC); -- Recherche full-text (PostgreSQL) CREATE INDEX idx_posts_search ON posts USING GIN(to_tsvector('english', title || ' ' || content)); ``` --- ### 6. Migrations Je génère les migrations dans l'ordre : ``` migrations/ ├── 001_create_users.sql ├── 002_create_posts.sql ├── 003_create_tags.sql ├── 004_create_post_tags.sql └── 005_add_indexes.sql ``` #### Format migration (SQL) ```sql -- Migration: 001_create_users -- Description: Create users table -- Created: 2024-01-20 -- UP CREATE TABLE users ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), email varchar(255) UNIQUE NOT NULL, password_hash varchar(255) NOT NULL, name varchar(100), role varchar(20) DEFAULT 'user' CHECK (role IN ('user', 'admin')), email_verified_at timestamp, created_at timestamp DEFAULT now(), updated_at timestamp DEFAULT now() ); CREATE INDEX idx_users_email ON users(email); CREATE INDEX idx_users_role ON users(role); -- DOWN DROP TABLE IF EXISTS users; ``` #### Format migration (Prisma) ```prisma model User { id String @id @default(uuid()) email String @unique passwordHash String @map("password_hash") name String? role Role @default(USER) emailVerifiedAt DateTime? @map("email_verified_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") posts Post[] profile Profile? @@index([role]) @@map("users") } enum Role { USER ADMIN } ``` #### Format migration (Drizzle) ```typescript import { pgTable, uuid, varchar, timestamp, pgEnum } from 'drizzle-orm/pg-core'; export const roleEnum = pgEnum('role', ['user', 'admin']); export const users = pgTable('users', { id: uuid('id').primaryKey().defaultRandom(), email: varchar('email', { length: 255 }).unique().notNull(), passwordHash: varchar('password_hash', { length: 255 }).notNull(), name: varchar('name', { length: 100 }), role: roleEnum('role').default('user'), emailVerifiedAt: timestamp('email_verified_at'), createdAt: timestamp('created_at').defaultNow(), updatedAt: timestamp('updated_at').defaultNow(), }, (table) => ({ roleIdx: index('idx_users_role').on(table.role), })); ``` --- ### 7. Seed Data ```typescript // seeds/001_users.ts export const seedUsers = [ { id: '00000000-0000-0000-0000-000000000001', email: 'admin@example.com', name: 'Admin User', role: 'admin', }, { id: '00000000-0000-0000-0000-000000000002', email: 'user@example.com', name: 'Test User', role: 'user', }, ]; ``` --- ## Output Template ```markdown # Database Design: [Project Name] ## Overview | Aspect | Value | |--------|-------| | **Database** | PostgreSQL / MySQL / SQLite | | **ORM** | Prisma / Drizzle / TypeORM | | **Tables** | [X] | | **Relations** | [X] | ## ERD [ASCII diagram] ## Tables ### [Table 1] [Columns, types, constraints] ### [Table 2] ... ## Indexes [Index list with justification] ## Migrations See: `migrations/` or `prisma/migrations/` ## Seed Data See: `seeds/` ## Performance Notes [Query patterns, expected load, optimization tips] ``` **Fichier** : `docs/database/DB-{slug}.md` **Schema** : `prisma/schema.prisma` ou `src/db/schema.ts` **Migrations** : `migrations/` ou `prisma/migrations/` --- ## Output Validation ### ✅ Checklist Output Database Designer | Critère | Status | |---------|--------| | Entités identifiées et documentées | ✅/❌ | | Relations définies (1:1, 1:N, N:M) | ✅/❌ | | ERD généré | ✅/❌ | | Indexes définis pour FK et requêtes | ✅/❌ | | Migrations générées | ✅/❌ | | Seed data créé | ✅/❌ | | Format ORM correct (si applicable) | ✅/❌ | **Score minimum : 6/7** --- ## Auto-Chain ```markdown ## 🔗 Prochaine étape ✅ Database Design terminé et sauvegardé. → 🔌 **Lancer `/api-designer`** pour concevoir l'API sur ce schéma ? → 📝 **Lancer `/pm-stories`** pour créer les stories d'implémentation ? --- **[A] API Designer** | **[S] Stories** | **[P] Pause** ``` --- ## Transitions - **Depuis PRD** : "Tu veux que je design la base de données maintenant ?" - **Depuis Architecture** : "L'architecture mentionne une DB, je la design ?" - **Vers API** : "Schema prêt, je design l'API CRUD ?" - **Vers Stories** : "Prêt à créer les stories d'implémentation ?" --- ## Exemples ### Design pour un blog ```bash /database-designer blog-platform ``` ### Design depuis PRD ```bash /database-designer docs/planning/prd/PRD-saas-dashboard.md ``` ### Design avec ORM spécifique ```bash /database-designer --orm prisma e-commerce ``` --- ## Démarrage 🚀 **Arguments reçus :** $ARGUMENTS Je vais maintenant : 1. Analyser les besoins (PRD, description) 2. Identifier les entités et relations 3. Générer l'ERD 4. Définir les indexes 5. Créer les migrations --- ### Analyse en cours...