Padrões de Desenvolvimento tinystruct
Padrões de arquitetura e implementação para construir módulos com o framework tinystruct Java – um framework leve e de alta performance que trata CLI e HTTP como cidadãos equivalentes, não exigindo um método main() e com configuração mínima.
Princípio Central
CLI e HTTP são cidadãos equivalentes. Todo método anotado com @Action deve, idealmente, poder ser executado tanto de um terminal quanto de um navegador da web sem modificação. Essa capacidade de "modo duplo" é a filosofia de design central do tinystruct.
Quando Ativar
Quando Usar
- Criando novos módulos
ApplicaçãoestendendoAbstractApplication. - Definindo rotas e ações de linha de comando usando
@Action. - Manipulando estado por requisição via
Context. - Realizando serialização JSON usando os componentes nativos
BuildereBuilders. - Trabalhando com persistência de banco de dados via POJOs
AbstractData. - Gerando POJOs a partir de tabelas de banco de dados usando o comando
generate. - Implementando Server-Sent Events (SSE) para push em tempo real.
- Manipulando upload de arquivos via dados multipart.
- Fazendo requisições HTTP de saída com
URLRequesteHTTPHandler. - Configurando conexões de banco de dados ou configurações do sistema em
application.properties. - Depurando conflitos de rota (Actions) ou análise de argumentos CLI.
Como Funciona
O framework tinystruct trata qualquer método anotado com @Action como um endpoint roteável tanto para terminal quanto para ambiente web. As aplicações são criadas estendendo AbstractApplication, que fornece hooks de ciclo de vida principais como init() e acesso ao Context da requisição.
O roteamento é gerenciado pelo ActionRegistry, que mapeia automaticamente segmentos de caminho para argumentos de método e injeta dependências. Para serviços somente de dados, os componentes nativos Builder e Builders devem ser usados para serialização JSON, mantendo uma pegada de zero dependências. A camada de banco de dados usa POJOs AbstractData pareados com arquivos de mapeamento XML para operações CRUD sem bibliotecas ORM externas.
Exemplos
Aplicação Básica (MyService)
public class MyService extends AbstractApplication {
@Override
public void init() {
this.setTemplateRequired(false); // Desabilita a busca por .view para apps de dados/API
}
@Override public String version() { return "1.0.0"; }
@Action("greet")
public String greet() {
return "Olá do tinystruct!";
}
// Parâmetro de caminho: GET /?q=greet/James OU bin/dispatcher greet/James
@Action("greet")
public String greet(String name) {
return "Olá, " + name + "!";
}
}
Desambiguação de Modo HTTP (login)
@Action(value = "login", mode = Mode.HTTP_POST)
public String doLogin(Request<?, ?> request) throws ApplicationException {
request.getSession().setAttribute("userId", "42");
return "Logado";
}
Manipulação de Dados JSON Nativa (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\":\"Conectado ao SSE\"}";
}
// Enviar para um cliente específico
String sessionId = getContext().getId();
Builder msg = new Builder();
msg.put("text", "Olá, usuário!");
SSEPushManager.getInstance().push(sessionId, msg);
// Transmissão para todos
// Transmissão para todos
SSEPushManager.getInstance().broadcast(msg);
Upload de Arquivo
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("Enviado: " + file.getFilename());
}
}
return "Upload OK";
}
Integração com Servidor MCP e Ferramentas
otinystruct fornece suporte nativo ao Model Context Protocol (MCP) a partir da versão 1.7.26 do SDK.
As APIs MCP (por exemplo, org.tinystruct.mcp.MCPTool, org.tinystruct.mcp.MCPServer, org.tinystruct.mcp.MCPException) estão incluídas diretamente na dependência principal:
<dependency>
<groupId>org.tinystruct</groupId>
<artifactId>tinystruct</artifactId>
<version>1.7.26</version>
</dependency>
AVISO DE SEGURANÇA (Injeção de Prompt): Os valores de retorno das ferramentas são alimentados diretamente de volta no contexto do modelo de IA. Você DEVE validar e sanitizar todos os argumentos fornecidos pelo chamador antes de incluí-los na string de retorno da ferramenta. A falha em sanitizar as entradas pode permitir que um atacante injete instruções adversariais (Injeção de Prompt) que sobrescrevam o comportamento do modelo. Sempre valide tamanho, conjuntos de caracteres e nulidade.
Para criar uma Ferramenta MCP:
- Estenda
org.tinystruct.mcp.MCPTool. - Anote operações com
@Actione declare parâmetros usando@Argumentdentro do arrayarguments. - Aceite parâmetros como argumentos explícitos do método que correspondam às chaves em
@Argument. (Não usegetContext().getAttribute(...)para argumentos de ferramenta).
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", "Uma ferramenta personalizada para demonstrar MCP");
}
@Action(
value = "custom/hello",
description = "Dizer olá para alguém",
arguments = {
@Argument(key = "name", description = "O nome a ser saudado", type = "string", optional = false)
}
)
public String hello(String name) throws MCPException {
// SEGURANÇA: Validar/sanitizar entradas da ferramenta antes de retornar ao modelo
// para prevenir vulnerabilidades de injeção de prompt.
if (name == null || name.length() > 50 || !name.matches("^[a-zA-Z0-9 ]+$")) {
throw new MCPException("Nome inválido fornecido");
}
return "Olá, " + name + "!";
}
}
Para implantar um Servidor MCP:
- Estenda
org.tinystruct.mcp.MCPServer. - Sobrescreva
init()e registre suas ferramentas usandothis.registerTool(). O framework automaticamente escaneia e mapeia os métodos@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";
}
}
Execute o servidor através do dispatcher:
bin/dispatcher start --import org.tinystruct.system.HttpServer --import com.example.MyMCPServer
Configuração
As configurações são gerenciadas em src/main/resources/application.properties.
# Banco de Dados
driver=org.h2.Driver
database.url=jdbc:h2:~/mydb
database.user=sa
database.password=
# Servidor
default.home.page=hello
server.port=8080
# Localidade
default.language=en_US
# Sessão (Redis para ambientes clusterizados)
# default.session.repository=org.tinystruct.http.RedisSessionRepository
# redis.host=127.0.0.1
# redis.port=6379
Acesse valores de configuração em sua aplicação:
String port = this.getConfiguration("server.port");
Bandeiras Vermelhas e Anti-padrões
| Sintoma | Padrão Correto |
|---|---|
Importando com.google.gson ou com.fasterxml.jackson | Use org.tinystruct.data.component.Builder / Builders. |
Usando List<Builder> para arrays JSON | Use Builders para evitar problemas de apagamento de tipo genérico. |
ApplicationRuntimeException: template not found | Chame setTemplateRequired(false) no init() para apps somente API. |
Anotando métodos private com @Action | Ações devem ser public para serem registradas pelo framework. |
Codificando main(String[] args) em aplicações | Use bin/dispatcher como ponto de entrada para todos os módulos. |
Registro manual no ActionRegistry | Prefira a anotação @Action para descoberta automática. |
| Ação não encontrada em tempo de execução | Certifique-se de que a classe é importada via --import ou listada em application.properties. |
| Argumento CLI não visível | Passe com --key value; acesse via getContext().getAttribute("--key"). |
| Dois métodos mesmo caminho, o errado é executado | Defina o mode explicitamente (ex.: HTTP_GET vs HTTP_POST) para desambiguar. |
Melhores Práticas
- Aplicações Granulares: Divida a lógica em aplicações menores e focadas, em vez de uma única classe monolítica.
- Configuração no
init(): Useinit()para configuração (config, DB) em vez do construtor. NÃO chamesetAction()— use a anotação@Action. - Consciência de Modo: Use o parâmetro
Modeem@Actionpara restringir operações sensíveis aCLIapenas ou a métodos HTTP específicos. - Contexto sobre Parâmetros: Para flags CLI opcionais, use
getContext().getAttribute("--flag")em vez de adicionar parâmetros à assinatura do método. - Eventos Assíncronos: Para tarefas pesadas acionadas por eventos, use
CompletableFuture.runAsync()dentro do manipulador de eventos.
Referência Técnica
Guias detalhados estão disponíveis no diretório references/:
- Arquitetura e Configuração — Abstrações, Mapa de Pacotes, Propriedades
- Roteamento e @Action — Detalhes da anotação, Modos, Parâmetros
- Manipulação de Dados — Builder, Builders, serialização e análise JSON
- Persistência de Banco de Dados — POJOs AbstractData, CRUD, XML de mapeamento, geração de POJOs
- Sistema e Uso — Context, Sessões, SSE, Upload de Arquivos, Eventos, Rede
- Padrões de Teste — Testes unitários e de integração HTTP com JUnit 5
Arquivos de Código Fonte de Referência (Internos)
src/main/java/org/tinystruct/AbstractApplication.java— Classe base principal com hooks de ciclo de vidasrc/main/java/org/tinystruct/system/annotation/Action.java— Anotação e Modossrc/main/java/org/tinystruct/application/ActionRegistry.java— Mecanismo de Roteamentosrc/main/java/org/tinystruct/data/component/Builder.java— Serializador JSON de objetosrc/main/java/org/tinystruct/data/component/Builders.java— Serializador JSON de arraysrc/main/java/org/tinystruct/data/component/AbstractData.java— Classe POJO base com CRUDsrc/main/java/org/tinystruct/data/Mapping.java— Analisador XML de mapeamentosrc/main/java/org/tinystruct/data/tools/MySQLGenerator.java— Referência de gerador de POJOsrc/main/java/org/tinystruct/data/component/FieldType.java— Mapeamentos SQL-para-Javasrc/main/java/org/tinystruct/data/component/Condition.java— Construtor de consulta SQL fluentesrc/main/java/org/tinystruct/http/SSEPushManager.java— Gerenciamento de conexões SSEsrc/test/java/org/tinystruct/application/ActionRegistryTest.java— Exemplos de teste do Registrysrc/test/java/org/tinystruct/system/HttpServerHttpModeTest.java— Padrões de teste de integração HTTP