# Architecture PeakSkills ## Principes Fondamentaux ### 1. Services (Backend) - ✅ TOUTES les requêtes PostgreSQL DOIVENT être dans les services/ - ✅ Les services sont la SEULE couche autorisée à communiquer avec la base de données - ✅ Chaque service doit avoir une interface claire - ✅ Les services doivent gérer les transactions - ✅ Les services doivent logger les erreurs - ✅ Les services doivent valider les données avant insertion - ❌ AUCUNE requête SQL en dehors des services ### 2. Routes API (Backend) - ✅ Les routes API doivent UNIQUEMENT utiliser les services - ✅ Les routes doivent gérer la validation des entrées - ✅ Les routes doivent retourner des réponses typées - ❌ AUCUNE requête SQL directe dans les routes ### 3. Clients HTTP (Frontend) - ✅ Les appels HTTP doivent être dans clients/domains/ - ✅ Chaque domaine a son propre client - ✅ Les clients doivent typer leurs réponses - ❌ AUCUNE logique métier dans les clients ### 4. Components (Frontend) - ✅ UI réutilisable dans components/ui/ - ✅ Composants feature dans leur dossier dédié - ✅ Export via index.ts - ❌ AUCUN appel direct aux services ## Exemple d'Architecture ### Service (Backend) ```typescript // services/skills-service.ts export class SkillsService { constructor(private pool: Pool) {} async getSkillsByCategory(categoryId: string): Promise { const client = await this.pool.connect(); try { await client.query("BEGIN"); const result = await client.query( ` SELECT s.*, c.name as category_name FROM skills s JOIN skill_categories c ON s.category_id = c.id WHERE c.id = $1 `, [categoryId] ); await client.query("COMMIT"); return result.rows; } catch (error) { await client.query("ROLLBACK"); logger.error("Failed to get skills by category", { error, categoryId }); throw new DatabaseError("Failed to get skills"); } finally { client.release(); } } } ``` ### Route API ```typescript // app/api/skills/[categoryId]/route.ts import { SkillsService } from "@/services/skills-service"; export async function GET( request: Request, { params: { categoryId } }: { params: { categoryId: string } } ) { try { const skillsService = new SkillsService(pool); const skills = await skillsService.getSkillsByCategory(categoryId); return Response.json(skills); } catch (error) { return Response.error(); } } ``` ### Client HTTP (Frontend) ```typescript // clients/domains/skills-client.ts export class SkillsClient { constructor(private httpClient: HttpClient) {} async getSkillsByCategory(categoryId: string): Promise { return this.httpClient.get(`/api/skills/${categoryId}`); } } ``` ### Composant (Frontend) ```typescript // components/skills/SkillList.tsx export const SkillList = ({ categoryId }: { categoryId: string }) => { const { skills, loading } = useSkills(categoryId); if (loading) return ; return (
{skills.map(skill => ( ))}
); }; ``` ### Hook (Frontend) ```typescript // hooks/useSkills.ts export const useSkills = (categoryId: string) => { const skillsClient = useSkillsClient(); const [skills, setSkills] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { skillsClient .getSkillsByCategory(categoryId) .then(setSkills) .finally(() => setLoading(false)); }, [categoryId]); return { skills, loading }; }; ``` ## Validation de l'Architecture Pour s'assurer que l'architecture est respectée : 1. **Review de Code** - Vérifier qu'aucune requête SQL n'est en dehors des services - Vérifier que les composants n'accèdent pas directement aux services - Vérifier que les types sont correctement utilisés 2. **Tests** - Tests unitaires pour les services - Tests d'intégration pour les routes API - Tests de composants pour le frontend 3. **Monitoring** - Logger les erreurs de service - Monitorer les performances des requêtes - Tracer les appels API