Patrons de développement tinystruct
Patrons d'architecture et d'implémentation pour construire des modules avec le framework Java tinystruct – un framework léger et hautes performances qui traite les interfaces CLI et HTTP comme des citoyens égaux, ne nécessitant aucune méthode main() et une configuration minimale.
Principe Fondamental
Les interfaces CLI et HTTP sont des citoyens égaux. Chaque méthode annotée avec @Action doit idéalement pouvoir être exécutée depuis un terminal et depuis un navigateur web sans modification. Cette capacité "double-mode" est la philosophie de conception centrale de tinystruct.
Quand Activer
Quand Utiliser
- Création de nouveaux modules
Applicationen étendantAbstractApplication. - Définition de routes et d'actions en ligne de commande avec
@Action. - Gestion d'état par requête via
Context. - Sérialisation JSON avec les composants natifs
BuilderetBuilders. - Travail avec la persistance via les POJO
AbstractData. - Génération de POJO à partir de tables de base de données avec la commande
generate. - Implémentation de Server-Sent Events (SSE) pour les mises à jour en temps réel.
- Gestion des téléchargements de fichiers via données multipart.
- Requêtes HTTP sortantes avec
URLRequestetHTTPHandler. - Configuration des connexions base de données ou des paramètres système dans
application.properties. - Débogage des conflits de routes (Actions) ou de l'analyse des arguments CLI.
Comment Ça Fonctionne
Le framework tinystruct traite toute méthode annotée avec @Action comme un point de terminaison routable à la fois pour les environnements terminal et web. Les applications sont créées en étendant AbstractApplication, qui fournit des hooks de cycle de vie essentiels comme init() et l'accès au Context de la requête.
Le routage est géré par ActionRegistry, qui mappe automatiquement les segments de chemin aux arguments de la méthode et injecte les dépendances. Pour les services uniquement de données, les composants natifs Builder et Builders doivent être utilisés pour la sérialisation JSON afin de maintenir une empreinte zéro dépendance. La couche base de données utilise des POJO AbstractData associés à des fichiers de mappage XML pour les opérations CRUD sans bibliothèques ORM externes.
Exemples
Application de Base (MyService)
public class MyService extends AbstractApplication {
@Override
public void init() {
this.setTemplateRequired(false); // Désactive la recherche .view pour les applications de données/API
}
@Override public String version() { return "1.0.0"; }
@Action("greet")
public String greet() {
return "Bonjour depuis tinystruct !";
}
// Paramètre de chemin : GET /?q=greet/James OU bin/dispatcher greet/James
@Action("greet")
public String greet(String name) {
return "Bonjour, " + name + " !";
}
}
Désambiguïsation en Mode HTTP (login)
@Action(value = "login", mode = Mode.HTTP_POST)
public String doLogin(Request<?, ?> request) throws ApplicationException {
request.getSession().setAttribute("userId", "42");
return "Connecté";
}
Gestion JSON Native (Builder + Builders)
import org.tinystruct.data.component.Builder;
import org.tinystruct.data.component.Builders;
@Action("api/data")
public String getData() throws ApplicationException {
Builders dataList = new Builders();
Builder item = new Builder();
item.put("id", 1);
item.put("name", "James");
dataList.add(item);
Builder response = new Builder();
response.put("status", "success");
response.put("data", dataList);
return response.toString(); // {"status":"success","data":[{"id":1,"name":"James"}]}
}
SSE (Server-Sent Events)
import org.tinystruct.http.SSEPushManager;
@Action("sse/connect")
public String connect() {
return "{\"type\":\"connect\",\"message\":\"Connecté au SSE\"}";
}
// Envoyer à un client spécifique
String sessionId = getContext().getId();
Builder msg = new Builder();
msg.put("text", "Bonjour, utilisateur !");
SSEPushManager.getInstance().push(sessionId, msg);
// Diffuser à tous
SSEPushManager.getInstance().broadcast(msg);
Téléchargement de Fichier
import org.tinystruct.data.FileEntity;
@Action(value = "upload", mode = Mode.HTTP_POST)
public String upload(Request<?, ?> request) throws ApplicationException {
List<FileEntity> files = request.getAttachments();
if (files != null) {
for (FileEntity file : files) {
System.out.println("Téléchargé : " + file.getFilename());
}
}
return "Téléchargement OK";
}
Intégration Serveur MCP et Outils
tinystruct offre un support natif du Model Context Protocol (MCP) à partir de la version 1.7.26 du SDK.
Les API MCP (par exemple org.tinystruct.mcp.MCPTool, org.tinystruct.mcp.MCPServer, org.tinystruct.mcp.MCPException) sont incluses directement dans la dépendance principale :
<dependency>
<groupId>org.tinystruct</groupId>
<artifactId>tinystruct</artifactId>
<version>1.7.26</version>
</dependency>
AVERTISSEMENT DE SÉCURITÉ (Injection de Prompt) : Les valeurs de retour des outils sont directement injectées dans le contexte du modèle d'IA. Vous DEVEZ valider et assainir tous les arguments fournis par l'appelant avant de les inclure dans la chaîne de retour de l'outil. Ne pas assainir les entrées peut permettre à un attaquant d'injecter des instructions adverses (Injection de Prompt) qui outrepassent le comportement du modèle. Validez toujours la longueur, les jeux de caractères et la nullité.
Pour créer un outil MCP :
- Étendez
org.tinystruct.mcp.MCPTool. - Annotez les opérations avec
@Actionet déclarez les paramètres en utilisant@Argumentdans le tableauarguments. - Acceptez les paramètres comme arguments explicites de la méthode correspondant aux clés dans
@Argument. (N'utilisez pasgetContext().getAttribute(...)pour les arguments d'outil).
import org.tinystruct.mcp.MCPTool;
import org.tinystruct.mcp.MCPException;
import org.tinystruct.system.annotation.Action;
import org.tinystruct.system.annotation.Argument;
public class MyCustomTool extends MCPTool {
public MyCustomTool() {
super("custom", "Un outil personnalisé pour démontrer MCP");
}
@Action(
value = "custom/hello",
description = "Dire bonjour à quelqu'un",
arguments = {
@Argument(key = "name", description = "Le nom à saluer", type = "string", optional = false)
}
)
public String hello(String name) throws MCPException {
// SÉCURITÉ : Validez/assainissez les entrées de l'outil avant de les renvoyer au modèle
// pour prévenir les vulnérabilités d'injection de prompt.
if (name == null || name.length() > 50 || !name.matches("^[a-zA-Z0-9 ]+$")) {
throw new MCPException("Nom fourni invalide");
}
return "Bonjour, " + name + " !";
}
}
Pour déployer un serveur MCP :
- Étendez
org.tinystruct.mcp.MCPServer. - Surchargez
init()et enregistrez vos outils en utilisantthis.registerTool(). Le framework scanne et mappe automatiquement les méthodes@Action.
import org.tinystruct.mcp.MCPServer;
public class MyMCPServer extends MCPServer {
@Override
public void init() {
super.init();
this.registerTool(new MyCustomTool());
}
@Override
public String version() {
return "1.0.0";
}
}
Lancez le serveur via le dispatcher :
bin/dispatcher start --import org.tinystruct.system.HttpServer --import com.example.MyMCPServer
Configuration
Les paramètres sont gérés dans src/main/resources/application.properties.
# Base de données
driver=org.h2.Driver
database.url=jdbc:h2:~/mydb
database.user=sa
database.password=
# Serveur
default.home.page=hello
server.port=8080
# Locale
default.language=en_US
# Session (Redis pour environnements clusterisés)
# default.session.repository=org.tinystruct.http.RedisSessionRepository
# redis.host=127.0.0.1
# redis.port=6379
Accédez aux valeurs de configuration dans votre application :
String port = this.getConfiguration("server.port");
Drapeaux Rouges & Anti-patrons
| Symptôme | Patron Correct |
|---|---|
Importer com.google.gson ou com.fasterxml.jackson | Utiliser org.tinystruct.data.component.Builder / Builders. |
Utiliser List<Builder> pour les tableaux JSON | Utiliser Builders pour éviter les problèmes d'effacement de type générique. |
ApplicationRuntimeException: template not found | Appeler setTemplateRequired(false) dans init() pour les applications API uniquement. |
Annoter des méthodes private avec @Action | Les actions doivent être public pour être enregistrées par le framework. |
Coder en dur main(String[] args) dans les applications | Utiliser bin/dispatcher comme point d'entrée pour tous les modules. |
Enregistrement manuel d'ActionRegistry | Préférer l'annotation @Action pour la découverte automatique. |
| Action introuvable à l'exécution | S'assurer que la classe est importée via --import ou listée dans application.properties. |
| Argument CLI non visible | Passer avec --key value ; accéder via getContext().getAttribute("--key"). |
| Deux méthodes même chemin, la mauvaise se déclenche | Définir un mode explicite (ex. HTTP_GET vs HTTP_POST) pour désambiguïser. |
Bonnes Pratiques
- Applications Granulaires : Diviser la logique en applications plus petites et ciblées plutôt qu'une seule classe monolithique.
- Configuration dans
init(): Utiliserinit()pour la configuration (config., base de données) plutôt que le constructeur. Ne PAS appelersetAction()— utiliser l'annotation@Action. - Conscience du Mode : Utiliser le paramètre
Modedans@Actionpour restreindre les opérations sensibles àCLIuniquement ou à des méthodes HTTP spécifiques. - Context plutôt que Paramètres : Pour les options CLI facultatives, utiliser
getContext().getAttribute("--flag")plutôt que d'ajouter des paramètres à la signature de la méthode. - Événements Asynchrones : Pour les tâches lourdes déclenchées par des événements, utiliser
CompletableFuture.runAsync()à l'intérieur du gestionnaire d'événements.
Référence Technique
Des guides détaillés sont disponibles dans le répertoire references/ :
- Architecture & Config — Abstractions, Plan des Paquetages, Propriétés
- Routage & @Action — Détails des annotations, Modes, Paramètres
- Gestion des Données — Builder, Builders, sérialisation & analyse JSON
- Persistance Base de Données — POJO AbstractData, CRUD, XML de mappage, génération de POJO
- Système & Utilisation — Context, Sessions, SSE, Téléchargements de Fichiers, Événements, Réseau
- Patrons de Test — Tests unitaires et d'intégration HTTP avec JUnit 5
Fichiers Source de Référence (Internes)
src/main/java/org/tinystruct/AbstractApplication.java— Classe de base principale avec hooks de cycle de viesrc/main/java/org/tinystruct/system/annotation/Action.java— Annotation et Modessrc/main/java/org/tinystruct/application/ActionRegistry.java— Moteur de routagesrc/main/java/org/tinystruct/data/component/Builder.java— Sérialiseur d'objet JSONsrc/main/java/org/tinystruct/data/component/Builders.java— Sérialiseur de tableau JSONsrc/main/java/org/tinystruct/data/component/AbstractData.java— Classe POJO de base avec CRUDsrc/main/java/org/tinystruct/data/Mapping.java— Analyseur XML de mappagesrc/main/java/org/tinystruct/data/tools/MySQLGenerator.java— Référence de générateur de POJOsrc/main/java/org/tinystruct/data/component/FieldType.java— Mappages SQL vers Javasrc/main/java/org/tinystruct/data/component/Condition.java— Constructeur de requête SQL fluidesrc/main/java/org/tinystruct/http/SSEPushManager.java— Gestion des connexions SSEsrc/test/java/org/tinystruct/application/ActionRegistryTest.java— Exemples de test du registresrc/test/java/org/tinystruct/system/HttpServerHttpModeTest.java— Patrons de test d'intégration HTTP