brief-20/docs/implementation/DATABASE_SCHEMA_PLAN.md
Avnyr f6f0888bd7 docs: add comprehensive project documentation files
Added detailed documentation files, including project overview, current status, specifications, implementation guide, and README structure. Organized content to improve navigation and streamline project understanding.
2025-05-15 17:08:53 +02:00

405 lines
13 KiB
Markdown

# Plan du Schéma de Base de Données
Ce document détaille le plan du schéma de base de données pour l'application de création de groupes, utilisant DrizzleORM avec PostgreSQL.
## 1. Vue d'Ensemble
Le schéma de base de données est conçu pour supporter les fonctionnalités suivantes :
- Gestion des utilisateurs et authentification
- Création et gestion de projets
- Gestion des personnes et de leurs attributs
- Création et gestion de groupes
- Système de tags pour catégoriser les personnes et les projets
## 2. Tables Principales
### 2.1 Table `users`
Stocke les informations des utilisateurs de l'application.
```typescript
// src/database/schema/users.ts
import { pgTable, uuid, varchar, timestamp, boolean } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: uuid('id').defaultRandom().primaryKey(),
githubId: varchar('github_id', { length: 255 }).notNull().unique(),
name: varchar('name', { length: 255 }).notNull(),
avatar: varchar('avatar', { length: 1024 }),
role: varchar('role', { length: 50 }).notNull().default('USER'),
gdprTimestamp: timestamp('gdpr_timestamp').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
```
### 2.2 Table `projects`
Stocke les informations des projets créés par les utilisateurs.
```typescript
// src/database/schema/projects.ts
import { pgTable, uuid, varchar, text, timestamp, boolean, jsonb } from 'drizzle-orm/pg-core';
import { users } from './users';
export const projects = pgTable('projects', {
id: uuid('id').defaultRandom().primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
description: text('description'),
settings: jsonb('settings').notNull().default({}),
userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
isPublic: boolean('is_public').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export type Project = typeof projects.$inferSelect;
export type NewProject = typeof projects.$inferInsert;
```
### 2.3 Table `persons`
Stocke les informations des personnes qui seront placées dans les groupes.
```typescript
// src/database/schema/persons.ts
import { pgTable, uuid, varchar, text, integer, timestamp, jsonb } from 'drizzle-orm/pg-core';
import { projects } from './projects';
export const persons = pgTable('persons', {
id: uuid('id').defaultRandom().primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
email: varchar('email', { length: 255 }),
technicalLevel: integer('technical_level'),
gender: varchar('gender', { length: 50 }),
attributes: jsonb('attributes').notNull().default({}),
projectId: uuid('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export type Person = typeof persons.$inferSelect;
export type NewPerson = typeof persons.$inferInsert;
```
### 2.4 Table `groups`
Stocke les informations des groupes créés dans le cadre d'un projet.
```typescript
// src/database/schema/groups.ts
import { pgTable, uuid, varchar, text, timestamp, jsonb } from 'drizzle-orm/pg-core';
import { projects } from './projects';
export const groups = pgTable('groups', {
id: uuid('id').defaultRandom().primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
description: text('description'),
settings: jsonb('settings').notNull().default({}),
projectId: uuid('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export type Group = typeof groups.$inferSelect;
export type NewGroup = typeof groups.$inferInsert;
```
### 2.5 Table `tags`
Stocke les tags qui peuvent être associés aux personnes et aux projets.
```typescript
// src/database/schema/tags.ts
import { pgTable, uuid, varchar, text, timestamp, pgEnum } from 'drizzle-orm/pg-core';
export const tagTypeEnum = pgEnum('tag_type', ['PROJECT', 'PERSON']);
export const tags = pgTable('tags', {
id: uuid('id').defaultRandom().primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
description: text('description'),
color: varchar('color', { length: 50 }),
type: tagTypeEnum('type').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export type Tag = typeof tags.$inferSelect;
export type NewTag = typeof tags.$inferInsert;
```
## 3. Tables de Relations
### 3.1 Table `personToGroup`
Relation many-to-many entre les personnes et les groupes.
```typescript
// src/database/schema/personToGroup.ts
import { pgTable, uuid, primaryKey } from 'drizzle-orm/pg-core';
import { persons } from './persons';
import { groups } from './groups';
export const personToGroup = pgTable('person_to_group', {
personId: uuid('person_id').notNull().references(() => persons.id, { onDelete: 'cascade' }),
groupId: uuid('group_id').notNull().references(() => groups.id, { onDelete: 'cascade' }),
}, (t) => ({
pk: primaryKey({ columns: [t.personId, t.groupId] }),
}));
```
### 3.2 Table `personToTag`
Relation many-to-many entre les personnes et les tags.
```typescript
// src/database/schema/personToTag.ts
import { pgTable, uuid, primaryKey } from 'drizzle-orm/pg-core';
import { persons } from './persons';
import { tags } from './tags';
export const personToTag = pgTable('person_to_tag', {
personId: uuid('person_id').notNull().references(() => persons.id, { onDelete: 'cascade' }),
tagId: uuid('tag_id').notNull().references(() => tags.id, { onDelete: 'cascade' }),
}, (t) => ({
pk: primaryKey({ columns: [t.personId, t.tagId] }),
}));
```
### 3.3 Table `projectToTag`
Relation many-to-many entre les projets et les tags.
```typescript
// src/database/schema/projectToTag.ts
import { pgTable, uuid, primaryKey } from 'drizzle-orm/pg-core';
import { projects } from './projects';
import { tags } from './tags';
export const projectToTag = pgTable('project_to_tag', {
projectId: uuid('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
tagId: uuid('tag_id').notNull().references(() => tags.id, { onDelete: 'cascade' }),
}, (t) => ({
pk: primaryKey({ columns: [t.projectId, t.tagId] }),
}));
```
### 3.4 Table `projectCollaborators`
Relation many-to-many entre les projets et les utilisateurs pour la collaboration.
```typescript
// src/database/schema/projectCollaborators.ts
import { pgTable, uuid, primaryKey, varchar } from 'drizzle-orm/pg-core';
import { projects } from './projects';
import { users } from './users';
export const projectCollaborators = pgTable('project_collaborators', {
projectId: uuid('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
userId: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
role: varchar('role', { length: 50 }).notNull().default('VIEWER'),
}, (t) => ({
pk: primaryKey({ columns: [t.projectId, t.userId] }),
}));
```
## 4. Fichier d'Index
Fichier qui exporte toutes les tables pour faciliter leur utilisation.
```typescript
// src/database/schema/index.ts
export * from './users';
export * from './projects';
export * from './persons';
export * from './groups';
export * from './tags';
export * from './personToGroup';
export * from './personToTag';
export * from './projectToTag';
export * from './projectCollaborators';
```
## 5. Configuration de DrizzleORM
### 5.1 Module de Base de Données
```typescript
// src/database/database.module.ts
import { Module, Global } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import * as schema from './schema';
@Global()
@Module({
imports: [ConfigModule],
providers: [
{
provide: 'DATABASE_CONNECTION',
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
const connectionString = configService.get<string>('DATABASE_URL');
const pool = new Pool({ connectionString });
return drizzle(pool, { schema });
},
},
],
exports: ['DATABASE_CONNECTION'],
})
export class DatabaseModule {}
```
## 6. Migrations
### 6.1 Configuration des Migrations
```typescript
// drizzle.config.ts
import type { Config } from 'drizzle-kit';
import * as dotenv from 'dotenv';
dotenv.config();
export default {
schema: './src/database/schema/*.ts',
out: './src/database/migrations',
driver: 'pg',
dbCredentials: {
connectionString: process.env.DATABASE_URL,
},
} satisfies Config;
```
### 6.2 Script de Migration
```typescript
// src/database/migrations/migrate.ts
import { drizzle } from 'drizzle-orm/node-postgres';
import { migrate } from 'drizzle-orm/node-postgres/migrator';
import { Pool } from 'pg';
import * as dotenv from 'dotenv';
dotenv.config();
async function main() {
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const db = drizzle(pool);
console.log('Running migrations...');
await migrate(db, { migrationsFolder: './src/database/migrations' });
console.log('Migrations completed successfully!');
await pool.end();
}
main().catch((err) => {
console.error('Migration failed!', err);
process.exit(1);
});
```
## 7. Optimisations
### 7.1 Indexation
Pour optimiser les performances des requêtes fréquentes, nous ajouterons des index sur les colonnes suivantes :
```typescript
// Exemple d'ajout d'index sur la table persons
import { pgTable, uuid, varchar, text, integer, timestamp, jsonb, index } from 'drizzle-orm/pg-core';
import { projects } from './projects';
export const persons = pgTable('persons', {
id: uuid('id').defaultRandom().primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
email: varchar('email', { length: 255 }),
technicalLevel: integer('technical_level'),
gender: varchar('gender', { length: 50 }),
attributes: jsonb('attributes').notNull().default({}),
projectId: uuid('project_id').notNull().references(() => projects.id, { onDelete: 'cascade' }),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
}, (table) => ({
nameIdx: index('person_name_idx').on(table.name),
projectIdIdx: index('person_project_id_idx').on(table.projectId),
technicalLevelIdx: index('person_technical_level_idx').on(table.technicalLevel),
}));
```
### 7.2 Types de Données Optimisés
Utilisation de types de données optimisés pour réduire l'espace de stockage et améliorer les performances :
- `uuid` pour les identifiants uniques
- `varchar` avec longueur limitée pour les chaînes de caractères
- `jsonb` pour les données structurées flexibles
- `timestamp` pour les dates et heures
### 7.3 Contraintes d'Intégrité
Utilisation de contraintes d'intégrité référentielle pour garantir la cohérence des données :
- Clés primaires sur toutes les tables
- Clés étrangères avec actions en cascade pour la suppression
- Contraintes d'unicité sur les colonnes appropriées
## 8. Requêtes Communes
### 8.1 Récupération d'un Projet avec ses Personnes et Groupes
```typescript
// Exemple de requête pour récupérer un projet avec ses personnes et groupes
const getProjectWithPersonsAndGroups = async (db, projectId) => {
const project = await db.query.projects.findFirst({
where: (projects, { eq }) => eq(projects.id, projectId),
with: {
persons: true,
groups: {
with: {
persons: true,
},
},
},
});
return project;
};
```
### 8.2 Récupération des Personnes avec leurs Tags
```typescript
// Exemple de requête pour récupérer les personnes avec leurs tags
const getPersonsWithTags = async (db, projectId) => {
const persons = await db.query.persons.findMany({
where: (persons, { eq }) => eq(persons.projectId, projectId),
with: {
tags: {
columns: {
name: true,
color: true,
},
},
},
});
return persons;
};
```
## 9. Conclusion
Ce schéma de base de données fournit une structure solide pour l'application de création de groupes, avec une conception qui prend en compte les performances, la flexibilité et l'intégrité des données. Les relations entre les entités sont clairement définies, et les types de données sont optimisés pour les besoins de l'application.
L'utilisation de DrizzleORM permet une intégration transparente avec NestJS et offre une expérience de développement type-safe, facilitant la maintenance et l'évolution du schéma au fil du temps.