All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 3m8s
PeakSkills - Architecture et Organisation du Code
Structure du Projet
peakSkills/
├── app/ # Pages Next.js et routes API
├── clients/ # Clients HTTP pour les appels externes
│ ├── base/ # Client HTTP de base
│ └── domains/ # Clients par domaine métier
├── components/ # Composants React
│ ├── ui/ # Composants UI réutilisables
│ └── [feature]/ # Composants spécifiques aux features
├── services/ # Services métier
├── lib/ # Types et utilitaires partagés
├── hooks/ # Hooks React personnalisés
├── scripts/ # Scripts utilitaires
│ └── migrations/ # Scripts de migration
├── data/ # Données statiques
└── styles/ # Styles globaux
Règles de Structure
1. Clients HTTP (clients/)
- Toute la logique d'appels HTTP doit être encapsulée dans des clients dédiés
- Chaque domaine métier a son propre client (ex:
auth-client.ts,skills-client.ts) - Les clients utilisent le client de base (
http-client.ts) pour les appels - Les clients ne contiennent que la logique d'appel, pas de logique métier
Exemple :
// clients/domains/skills-client.ts
export class SkillsClient {
constructor(private httpClient: HttpClient) {}
async getSkills(): Promise<Skill[]> {
return this.httpClient.get("/api/skills");
}
}
2. Services Backend (services/)
- SEULE couche autorisée à faire des requêtes PostgreSQL
- Contiennent toute la logique métier et d'accès aux données
- Implémentent une interface claire
- Organisés par domaine métier
- Gèrent les transactions
- Valident les données
- Utilisent le pool de connexion de database.ts
Exemple :
// services/skills-service.ts
export interface ISkillsService {
getSkillsByCategory(category: string): Promise<Skill[]>;
}
export class SkillsService implements ISkillsService {
constructor(private pool: Pool) {}
async getSkillsByCategory(category: string): Promise<Skill[]> {
const 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
`;
const result = await this.pool.query(query, [category]);
return result.rows;
}
}
3. Composants (components/)
- Composants UI réutilisables dans
ui/ - Composants spécifiques dans leur dossier feature
- Export via
index.ts - Utilisation de TypeScript (
.tsx)
4. Pages (app/)
- Structure selon le routing Next.js
- Utilisation des composants
- Pas de logique métier directe
5. Types (lib/)
- Types partagés dans
types.ts - Types spécifiques dans
[domain]-types.ts - Interfaces commencent par "I"
Bonnes Pratiques
-
Séparation des Responsabilités
- Les composants gèrent l'UI
- Les services gèrent la logique métier
- Les clients gèrent les appels HTTP
-
Typage
- Tout doit être typé
- Utiliser des interfaces pour les contrats
- Éviter
any
-
Organisation du Code
- Un fichier = une responsabilité
- Export via
index.ts - Documentation en français
-
Tests
- Tests unitaires à côté du code
- Tests d'intégration dans
__tests__ - Mocks dans
__mocks__
Patterns à Éviter
❌ Ne pas mettre de logique métier dans les composants ❌ Ne pas faire d'appels HTTP directs ❌ Ne pas dupliquer les types ❌ Ne pas mélanger les responsabilités
Exemples
✅ Bon Pattern
// components/skills/SkillList.tsx
export const SkillList = () => {
const { skills } = useSkills(); // Hook personnalisé
return <div>{skills.map(skill => <SkillCard skill={skill} />)}</div>;
};
// hooks/useSkills.ts
export const useSkills = () => {
const skillsService = useSkillsService();
const [skills, setSkills] = useState<Skill[]>([]);
useEffect(() => {
skillsService.getSkills().then(setSkills);
}, []);
return { skills };
};
❌ Mauvais Pattern
// components/skills/SkillList.tsx
export const SkillList = () => {
const [skills, setSkills] = useState<Skill[]>([]);
useEffect(() => {
// ❌ Appel HTTP direct dans le composant
fetch('/api/skills').then(res => res.json()).then(setSkills);
}, []);
return <div>{skills.map(skill => <SkillCard skill={skill} />)}</div>;
};
Description
Languages
TypeScript
95.3%
JavaScript
2.6%
CSS
1.2%
PLpgSQL
0.7%
Dockerfile
0.2%