Piyush-Nagar/coding-agents-skills

Structure du backend en **Package par Fonctionnalité**, réparti sur quatre modules frères. Ce document est la source de vérité pour la localisation du nouveau code.

Qu'est-ce que coding-agents-skills ?

Cette documentation décrit la structure des services backend organisée selon le principe **Package par Fonctionnalité**, avec quatre modules frères. L'objectif est de garantir que les modifications apportées à une fonctionnalité (par exemple, "commandes") se trouvent au même endroit, plutôt que dispersées dans des dossiers techniques comme `controllers/`, `services/` ou `repositories/`. Les quatre modules sont : `capabilities/` (logique métier, contrôleurs, DTOs), `db/` (persistance avec entités JPA, repositories et DAOs), `client/` (intégrations sortantes vers d'autres microservices) et `starter/` (point d'entrée Spring Boot, configuration transversale). La règle de dépendance est stricte : `starter` → `capabilities` → { `db`, `client` }, et `capabilities` ne peut dépendre que des interfaces `*Repo` et `*Client`, jamais de leurs implémentations. La documentation détaille la création de sous-fonctionnalités par API, la séparation interface/implémentation pour les contrôleurs et les repositories, ainsi que les règles de nommage et de granularité adaptables à toute langue de programmation (Java, Kotlin, Go, Python, etc.).

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Piyush-Nagar/coding-agents-skills/tree/HEAD/backend/core

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Structure des Packages

Nos services backend suivent un modèle Package par Fonctionnalité organisé en quatre modules frères. Ce document est la source de vérité pour la localisation du nouveau code.

Note linguistique. Les exemples ci-dessous sont rédigés en Java/Spring Boot car c'est notre stack backend principal, mais la structure elle-même est agnostique au langage et obligatoire pour chaque langage backend que nous utilisons (Kotlin, Go, Python, Node, etc.). Traduisez les extensions de fichiers et les annotations de framework ; conservez l'agencement des dossiers, les suffixes de nommage (*Db, *Repo, *RepoImpl, *Dao, *Controller, *ControllerImpl) et la séparation interface/implémentation inchangés.

Principe fondamental

Grouper par fonctionnalité, pas par couche technique. Une modification de la fonctionnalité "commandes" doit concerner des fichiers qui se trouvent les uns à côté des autres, et non des fichiers dispersés dans les packages controllers/, services/, repositories/. Le regroupement par couche technique se fait à la frontière des modules (db, client, capabilities, starter), et le package par fonctionnalité se produit à l'intérieur de chaque module.

Les quatre modules

Chaque service est composé de ces quatre modules frères :

<service>/
├── capabilities/   # fonctionnalité/logique métier — le cerveau
├── db/             # persistance : entités, repositories, migrations
├── client/         # intégrations sortantes avec les systèmes externes
└── starter/        # Lanceur Spring Boot — assemble tout et exécute

capabilities/ — logique métier

Chaque fonctionnalité a son propre package, et à l'intérieur de chaque fonctionnalité, il y a un sous-package par point d'API (getById, create, updateById, getByQuery, …). Chaque sous-fonctionnalité possède trois dossiers frères :

  • service/ — la classe de service unique pour cette API
  • dto/ — request/ et response/ (selon le cas)
  • mapper/ — traduit les entités *Db ↔ DTOs (dans les deux sens) pour cette seule sous-fonctionnalité

Le contrôleur de la fonctionnalité se trouve à la racine du package de la fonctionnalité, divisé en une interface et une implémentation comme pour les repositories :

  • capabilities/<feature>/<Feature>Controller.java — interface ; déclare les routes, les verbes HTTP, les types de requête/réponse, les annotations de documentation API
  • capabilities/<feature>/<Feature>ControllerImpl.java — implémentation ; porte l'annotation du framework (@RestController) et délègue chaque route au service de sous-fonctionnalité correspondant

Partagé par toutes les sous-fonctionnalités de cette fonctionnalité. Voir Contrôleurs.

  • Dépend de : db (uniquement via les interfaces *Repo — voir Règle d'utilisation de db), client
  • Ne dépend jamais de : starter
  • Ne contient pas de classe main, ni de @SpringBootApplication
  • Possède : contrôleurs HTTP, DTOs de requête/réponse d'API, mappers, services

db/ — persistance

Organisé un dossier par table. Chaque dossier de table a exactement trois sous-dossiers :

db/<tableName>/
├── model/
│   ├── <EntityName>Db.java          # entité JPA principale
│   └── <Other>Db.java               # sous-modèles (types intégrés, entités enfants, projections)
├── dao/
│   └── <EntityName>Dao.java         # accès aux données bas niveau (Criteria/JPQL/requêtes natives)
└── repository/
    ├── <EntityName>Repo.java        # INTERFACE publique — le seul type visible par capabilities
    └── impl/
        └── <EntityName>RepoImpl.java # compose Spring Data + Dao derrière l'interface

Convention de nommage

CoucheSuffixeExemple
Entité JPADbTestcaseDb
Interface du repositoryRepoTestcaseRepo
Implémentation du repositoryRepoImplTestcaseRepoImpl
Assistant d'accès aux donnéesDaoTestcaseDao

Règle d'utilisation de db

Capabilities doivent dépendre uniquement des interfaces *Repo. *RepoImpl et *Dao sont internes — jamais injectés ou référencés depuis l'extérieur du package db/<tableName>/. C'est ce qui permet de rendre la couche de persistance interchangeable et de concentrer les tests de capabilities sur le contrat.

  • Dépend de : rien dans ce service
  • Expose : les interfaces *Repo et les types de modèles *Db
  • Ne contient jamais : de logique métier, de transformation en DTOs d'API

client/ — intégrations sortantes

Contient le code qui appelle les autres services dont ce service dépend. Principalement un sous-dossier par microservice dépendant de notre propre plateforme (paymentService, userService, executionService, …). Les wrappers de SDK tiers ou les producteurs de messages peuvent aussi se trouver ici, mais le modèle dominant — et la raison d'être de ce module — est celui des clients de microservices dépendants.

client/<microserviceName>/
├── <Microservice>Client.java       # interface — ce que capabilities utilise
├── impl/
│   └── <Microservice>ClientImpl.java   # impl : appel HTTP/gRPC/SDK, tentatives, timeouts
├── dto/                            # formes de requête/réponse pour l'API DE CE microservice
│   ├── request/
│   └── response/
└── mapper/                         # facultatif : traduit entre leurs formes et les nôtres

Règles

  • Un sous-dossier par microservice dépendant, nommé d'après le service. Ne regroupez pas plusieurs microservices dans un même package même s'ils sont "liés".

  • Capabilities dépendent uniquement des interfaces *Client. *ClientImpl est interne — même règle que pour *Repo / *RepoImpl.

  • L'interface client expose l'API du microservice en termes de ses propres DTOs ; les capabilities peuvent les adapter si nécessaire.

  • Les préoccupations transversales (tentatives, circuit breaking, journalisation requête/réponse, en-têtes d'authentification) appartiennent à impl/ — les capabilities ne doivent jamais les voir.

  • Dépend de : rien dans ce service

  • Expose : les interfaces *Client et leurs DTOs de requête/réponse

  • Ne contient jamais : de logique métier, d'orchestration de capabilities

starter/ — racine de composition

Le seul module qui s'exécute. Contient @SpringBootApplication, les gestionnaires d'exception REST globaux, la configuration de sécurité, les filtres, et application.yml. Pas de contrôleurs — ils vivent dans capabilities/.

  • Dépend de : capabilities (et transitivement db, client)
  • Possède : l'artefact déployable, la configuration web transversale (sécurité, CORS, gestion des erreurs)
  • Ne possède jamais : de contrôleurs de fonctionnalité ou de DTOs de fonctionnalité

Règle de dépendance

starter → capabilities → { db, client }

Les flèches vont dans un seul sens. db et client n'importent jamais depuis capabilities ou starter. capabilities n'importe jamais depuis starter.

Contrôleurs

Il y a un contrôleur par fonctionnalité, pas par sous-fonctionnalité, et il se trouve à la racine du package de la fonctionnalité dans capabilities/. Il est divisé en deux fichiers, reflétant la séparation *Repo / *RepoImpl :

  • capabilities/<feature>/<Feature>Controller.java — interface. Déclare les routes, les verbes HTTP, les paramètres de chemin/requête, les types de DTOs de requête/réponse et les annotations de documentation API (OpenAPI/Swagger). Quiconque lit l'interface devrait voir l'intégralité du contrat HTTP.
  • capabilities/<feature>/<Feature>ControllerImpl.java — implémentation. Porte l'annotation du framework (@RestController dans Spring), est la cible d'injection pour les services de sous-fonctionnalité, et contient une méthode courte par route qui appelle le service/ correspondant et retourne le résultat. Pas de logique au-delà de la délégation.

Pourquoi la séparation ? L'interface est le contrat API — stable, documenté, testable isolément. L'implémentation est le câblage — elle peut changer sans modifier le contrat. Même raisonnement que *Repo vs *RepoImpl.

Le contrôleur délègue chaque route HTTP au service de sous-fonctionnalité correspondant dans capabilities/<feature>/<sub-feature>/service/. Les DTOs de requête/réponse appartiennent à la sous-fonctionnalité (capabilities/<feature>/<sub-feature>/dto/) ; le contrôleur n'est qu'un adaptateur HTTP léger.

Pourquoi dans capabilities/ et pas dans starter/ ? Le contrôleur, son service et ses DTOs changent tous ensemble lorsqu'une API change — les garder dans un même module préserve la promesse du package par fonctionnalité : "une fonctionnalité, un endroit". starter/ est réservé au câblage transversal (sécurité, gestionnaires d'erreurs, point d'entrée de l'application), pas au code web par fonctionnalité.

Exemple concret : une fonctionnalité testcase

L'arborescence ci-dessous utilise des extensions .java pour l'illustration. Remplacez l'extension de fichier et les idiomes de framework de votre langage — Kotlin .kt, packages Go, modules Python, etc. — mais conservez l'agencement des dossiers et les suffixes de nommage identiques.

testcase-service/
├── capabilities/
│   └── com/example/testcase/capabilities/
│       └── testcase/                          # fonctionnalité principale
│           ├── TestcaseController.java        # interface — contrat HTTP, déclarations de routes, docs API
│           ├── TestcaseControllerImpl.java    # impl — @RestController, délègue aux services de sous-fonctionnalités
│           ├── getById/                       # sous-fonctionnalité = une API
│           │   ├── service/
│           │   │   └── GetTestcaseByIdService.java
│           │   ├── dto/
│           │   │   └── response/
│           │   │       └── GetTestcaseByIdResponse.java
│           │   └── mapper/
│           │       └── GetTestcaseByIdMapper.java     # TestcaseDb → réponse
│           ├── create/
│           │   ├── service/
│           │   │   └── CreateTestcaseService.java
│           │   ├── dto/
│           │   │   ├── request/
│           │   │   │   └── CreateTestcaseRequest.java
│           │   │   └── response/
│           │   │       └── CreateTestcaseResponse.java
│           │   └── mapper/
│           │       └── CreateTestcaseMapper.java      # requête → TestcaseDb, TestcaseDb → réponse
│           ├── updateById/
│           │   ├── service/
│           │   │   └── UpdateTestcaseByIdService.java
│           │   ├── dto/
│           │   │   ├── request/
│           │   │   │   └── UpdateTestcaseByIdRequest.java
│           │   │   └── response/
│           │   │       └── UpdateTestcaseByIdResponse.java
│           │   └── mapper/
│           │       └── UpdateTestcaseByIdMapper.java
│           └── getByQuery/
│               ├── service/
│               │   └── GetTestcaseByQueryService.java
│               ├── dto/
│               │   ├── request/
│               │   │   └── GetTestcaseByQueryRequest.java
│               │   └── response/
│               │       └── GetTestcaseByQueryResponse.java
│               └── mapper/
│                   └── GetTestcaseByQueryMapper.java
│
├── db/                                        # un dossier par table
│   └── com/example/testcase/db/
│       └── testcase/                          # nom de la table
│           ├── model/
│           │   ├── TestcaseDb.java            # entité JPA — principale
│           │   └── TestcaseStepDb.java        # sous-modèle (enfant/intégré)
│           ├── dao/
│           │   └── TestcaseDao.java           # assistant de requête/criteria, utilisation privée au package
│           └── repository/
│               ├── TestcaseRepo.java          # interface — capabilities dépendent de CECI
│               └── impl/
│                   └── TestcaseRepoImpl.java  # compose Spring Data + TestcaseDao
│
├── client/                                    # un dossier par microservice dépendant
│   └── com/example/testcase/client/
│       ├── executionService/                  # microservice dépendant que nous appelons
│       │   ├── ExecutionServiceClient.java        # interface
│       │   ├── impl/
│       │   │   └── ExecutionServiceClientImpl.java  # appel HTTP/gRPC, tentatives, timeouts
│       │   ├── dto/
│       │   │   ├── request/
│       │   │   │   └── RunTestcaseRequest.java
│       │   │   └── response/
│       │   │       └── RunTestcaseResponse.java
│       │   └── mapper/
│       │       └── ExecutionServiceMapper.java
│       └── userService/                      # un autre microservice dépendant
│           ├── UserServiceClient.java
│           └── impl/
│               └── UserServiceClientImpl.java
│
└── starter/                                  # câblage transversal uniquement — pas de contrôleurs
    └── com/example/testcase/
        ├── TestcaseServiceApplication.java
        ├── config/
        │   ├── SecurityConfig.java
        │   └── WebConfig.java
        ├── error/
        │   └── GlobalExceptionHandler.java   # @ControllerAdvice — transversal, pas par fonctionnalité
        └── resources/application.yml

Règles empiriques

  • Ajouter une nouvelle API à une fonctionnalité existante ? Créez un nouveau package de sous-fonctionnalité dans capabilities/<feature>/ avec ses propres service/, dto/{request,response}/, et mapper/, puis ajoutez la déclaration de route dans <Feature>Controller.java (interface) et la méthode de délégation dans <Feature>ControllerImpl.java. Ne créez pas un second contrôleur.
  • Ajouter une toute nouvelle fonctionnalité ? Créez le package de fonctionnalité dans capabilities/ (contrôleur à la racine, sous-fonctionnalités par API), et le dossier de table correspondant dans db/<tableName>/ avec model/, dao/, repository/{impl/}. Ajoutez un client uniquement si vous appelez un système externe. Ne touchez pas à starter/.
  • Injectez toujours *Repo, jamais *RepoImpl ou *Dao. Les capabilities dépendent uniquement de l'interface. *RepoImpl et *Dao sont internes à db/<tableName>/ — ils existent pour vous permettre de changer la stratégie de requête ou le stockage sous-jacent sans modifier le code de capability.
  • Injectez toujours *Client, jamais *ClientImpl. Même règle pour les appels aux microservices dépendants. Les capabilities dépendent de l'interface ; le transport (HTTP, gRPC, SDK) vit derrière impl/.
  • Un dossier par microservice dépendant dans client/. Nommez le dossier d'après le microservice. Ne regroupez pas "tous les services internes" dans un seul package.
  • La granularité des sous-fonctionnalités s'arrête à capabilities/. db/ est organisé par table ; client/ par système externe. Diviser l'un ou l'autre par API disperserait un code cohérent.
  • Les DTOs de requête/réponse et leur mapper sont spécifiques à chaque sous-fonctionnalité, jamais partagés. Si create et updateById se ressemblent aujourd'hui, gardez deux DTOs et deux mappers. Le partage couple deux API qui dériveront indépendamment.
  • Les mappers convertissent entité ↔ DTO directement. Pas de couche de modèle de domaine intermédiaire. <SubFeature>Mapper convertit *Db ↔ les DTOs de requête/réponse de cette sous-fonctionnalité.
  • Trois sous-fonctionnalités ont un code similaire ? Résistez à la tentation d'extraire un service ou un mapper partagé tant que vous n'avez pas le même modèle dans quatre endroits ou plus.
  • Réutilisation entre fonctionnalités ? Placez les utilitaires partagés dans un package common/ à l'intérieur de capabilities/.

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

Skills associés