Files
peakskills/ARCHITECTURE.md
Julien Froidefond 03d49aa16a fix: improve skill category syncing and data structure
- 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.
2025-08-26 13:21:08 +02:00

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 :

  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