- Enhanced skill category syncing by ensuring id and name properties are included, boosting data integrity. - Cleaned up admin exports related to skill categories for better maintainability.
4.1 KiB
4.1 KiB
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)
// services/skills-service.ts
export class SkillsService {
constructor(private pool: Pool) {}
async getSkillsByCategory(categoryId: string): Promise<Skill[]> {
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
// 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)
// clients/domains/skills-client.ts
export class SkillsClient {
constructor(private httpClient: HttpClient) {}
async getSkillsByCategory(categoryId: string): Promise<Skill[]> {
return this.httpClient.get(`/api/skills/${categoryId}`);
}
}
Composant (Frontend)
// components/skills/SkillList.tsx
export const SkillList = ({ categoryId }: { categoryId: string }) => {
const { skills, loading } = useSkills(categoryId);
if (loading) return <Spinner />;
return (
<div>
{skills.map(skill => (
<SkillCard key={skill.id} skill={skill} />
))}
</div>
);
};
Hook (Frontend)
// hooks/useSkills.ts
export const useSkills = (categoryId: string) => {
const skillsClient = useSkillsClient();
const [skills, setSkills] = useState<Skill[]>([]);
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 :
-
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
-
Tests
- Tests unitaires pour les services
- Tests d'intégration pour les routes API
- Tests de composants pour le frontend
-
Monitoring
- Logger les erreurs de service
- Monitorer les performances des requêtes
- Tracer les appels API