Aperçu
Explique le contexte et les principes qui ont permis d'éliminer le goulot d'étranglement de la rédaction de deux fois le même contrat API et types lors du développement simultané du backend et du frontend, en remplaçant les tâches de développement répétitives par une génération basée sur la source.
Histoire d'origine
Je travaille à la fois sur le backend et le frontend. Lors de la création d'une fonctionnalité, j'ai dû écrire des points de terminaison et des DTO de requête/réponse dans Java WebFlux, puis écrire à nouveau le même URL et type TypeScript dans le frontend. Pour aligner la documentation Swagger, il a fallu transférer les informations déjà présentes dans le backend dans un autre format.
Le plus grand goulot d'étranglement était de réécrire le contrat API créé par le backend dans un format utilisable par le frontend. Si l'URL, la méthode HTTP, le paramètre de chemin/requête ou la structure de requête/réponse étaient modifiés différemment des deux côtés, le problème était découvert trop tard, après la compilation. Au-delà de la simple gêne, le fait que la même information soit gérée deux fois par des humains a causé des incohérences plus importantes.
Je voulais aussi réduire la répétition lors de la création de RouterFunction et de handler. Si j'ajoutais une référence comme ApiAccountHandler::search dans le routeur, je pensais qu'un squelette de classe et de méthode handler inexistants pourrait être créé automatiquement. Écrire le nom du champ d'entité comme une chaîne de caractères, par exemple "username", lors de la rédaction d'une requête MongoDB était également fastidieux et sujet aux fautes de frappe, donc j'ai inclus une fonctionnalité pour lire la source d'entité et générer un enum Java dans le même outil.
Ainsi, la génération de Swagger/OpenAPI, la création de squelette de handler et la génération d'enum de champ d'entité ont été réalisées en premier, puis j'ai ajouté la génération d'AsyncAPI en utilisant RSocket. Bien que cela puisse sembler être des idées distinctes, le point de départ est le même. Il s'agit d'éviter que les faits déjà notés dans le code backend soient réécrits par des humains, et de permettre aux outils de développement de générer les parties lisibles par machine.
Philosophie de développement
Je me base sur le code source backend.
J'utilise les informations déjà présentes dans RouterFunction, handler, DTO et entité comme source pour la documentation API et le code généré. L'essentiel est de ne pas gérer le même contrat dans un fichier séparé.
Je choisis l'automatisation au moment du développement plutôt que la magie d'exécution.
Ce n'est pas un framework qui intercepte les requêtes de production, mais qui analyse la source dans un environnement de développement local et génère des fichiers réels. Vous pouvez vérifier les résultats visuellement et les gérer par version.
Je suis prévisible dans le respect des conventions de projet.
Je ne vise pas un compilateur universel qui comprend tout le code Java. Je privilégie l'analyse de la structure des points de terminaison fonctionnels WebFlux que j'utilise réellement avec des règles claires.
J'élimine les sections de connexion répétées.
Je crée Swagger à partir des points de terminaison backend, et le générateur frontend lit ce document pour créer le service et le type, le tout dans une chaîne d'automatisation.
Que lire dans le backend
source de RouterFunction
Lit la méthode HTTP, le chemin imbriqué, la référence de méthode de gestionnaire et le prédicat.
Source du gestionnaire
Lit le corps de la requête, les valeurs de requête/chemin et le type de publication de réponse.
DTO de requête / réponse
Connecte le type Java déjà écrit dans le backend au schéma OpenAPI.
Source d'entité Mongo
Lit le nom brut de stockage du champ Java et de @Field, ainsi que la collection @Document.
Contrôleur RSocket
Lit la route @MessageMapping et le type de charge utile de requête/réponse.
Que créer au lieu de réécrire
swagger.json
Transforme le point de terminaison REST en un contrat API utilisable par le service frontal et le générateur de type TypeScript.
github.com/joohyoungkim19940805/typed-rx-http
Transforme la route RSocket et la charge utile en un contrat lisible par le générateur de client RSocket frontal.
Source du gestionnaire
Génère et corrige la structure de classe et de méthode en fonction de la référence de gestionnaire écrite dans RouterFunction.
{Entity}Fields enum
Crée une énumération pour le champ Java de l'entité et le nom brut de stockage afin d'éviter de répéter les noms de champ de chaîne.
CollectionNames enum
Génère pour pouvoir utiliser le nom de collection déclaré dans @Document au lieu d'une chaîne.
Flux d'automatisation dans le projet actuel
• Écrit RouterFunction, gestionnaire et DTO de requête/réponse Java dans le backend.
• Le watcher du profil local détecte les changements de source et met à jour swagger.json ou asyncapi-rsocket.json.
• Le script de génération @byeolnaerim/typed-rx-http lit la documentation et génère des fonctions de service et des types TypeScript.
• Dans le code de l'écran, les fonctions générées sont importées et utilisées sans réécrire l'URL et le type de réponse.
• Lorsque l'entité change, l'énumération des champs utilisés dans la requête et l'énumération de collection sont également mises à jour.