tinystruct 開発パターン
tinystruct Java フレームワークを使用してモジュールを構築するためのアーキテクチャと実装パターン。これは、CLI と HTTP を同等の機能として扱い、main() メソッドを必要とせず、最小限の設定で動作する軽量かつ高性能なフレームワークです。
基本原則
CLI と HTTP は同等の機能です。 @Action でアノテーションされた各メソッドは、理想的には、変更を加えることなくターミナルと Web ブラウザの両方から実行可能であるべきです。この「デュアルモード」機能が、tinystruct の設計哲学の中核です。
アクティベートするタイミング
使用する場合
AbstractApplicationを拡張して新しいApplicationモジュールを作成する場合。@Actionを使用してルートとコマンドラインアクションを定義する場合。Contextを介してリクエストごとの状態を処理する場合。- ネイティブの
BuilderおよびBuildersコンポーネントを使用して JSON シリアライゼーションを実行する場合。 AbstractDataPOJO を介してデータベース永続化を扱う場合。generateコマンドを使用してデータベーステーブルから POJO を生成する場合。- リアルタイムプッシュのためのサーバー送信イベント (SSE) を実装する場合。
- マルチパートデータを介したファイルアップロードを処理する場合。
URLRequestとHTTPHandlerを使用してアウトバウンド HTTP リクエストを行う場合。application.propertiesでデータベース接続またはシステム設定を構成する場合。- ルーティングの競合 (Actions) または CLI 引数の解析をデバッグする場合。
仕組み
tinystruct フレームワークは、@Action でアノテーションされた任意のメソッドを、ターミナル環境と Web 環境の両方でルーティング可能なエンドポイントとして扱います。アプリケーションは AbstractApplication を拡張して作成され、これにより init() などのコアライフサイクルフックとリクエスト Context へのアクセスが提供されます。
ルーティングは ActionRegistry によって処理され、パスセグメントをメソッド引数に自動的にマッピングし、依存関係を注入します。データのみのサービスの場合、依存関係ゼロのフットプリントを維持するために、ネイティブの Builder および Builders コンポーネントを JSON シリアライゼーションに使用する必要があります。データベース層は、外部 ORM ライブラリなしで CRUD 操作を行うために、XML マッピングファイルと組み合わせた AbstractData POJO を使用します。
例
基本的なアプリケーション (MyService)
public class MyService extends AbstractApplication {
@Override
public void init() {
this.setTemplateRequired(false); // データ/API アプリの .view ルックアップを無効化
}
@Override public String version() { return "1.0.0"; }
@Action("greet")
public String greet() {
return "Hello from tinystruct!";
}
// パスパラメータ: GET /?q=greet/James または bin/dispatcher greet/James
@Action("greet")
public String greet(String name) {
return "Hello, " + name + "!";
}
}
HTTP モードの区別 (ログイン)
@Action(value = "login", mode = Mode.HTTP_POST)
public String doLogin(Request<?, ?> request) throws ApplicationException {
request.getSession().setAttribute("userId", "42");
return "Logged in";
}
ネイティブ JSON データ処理 (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 (サーバー送信イベント)
import org.tinystruct.http.SSEPushManager;
@Action("sse/connect")
public String connect() {
return "{\"type\":\"connect\",\"message\":\"Connected to SSE\"}";
}
// 特定のクライアントにプッシュ
String sessionId = getContext().getId();
Builder msg = new Builder();
msg.put("text", "Hello, user!");
SSEPushManager.getInstance().push(sessionId, msg);
// 全員にブロードキャスト
SSEPushManager.getInstance().broadcast(msg);
ファイルアップロード
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("Uploaded: " + file.getFilename());
}
}
return "Upload OK";
}
MCP サーバーとツールの統合
tinystruct は、SDK バージョン 1.7.26 以降、Model Context Protocol (MCP) のネイティブサポートを提供します。
MCP API (org.tinystruct.mcp.MCPTool、org.tinystruct.mcp.MCPServer、org.tinystruct.mcp.MCPException など) は、コア依存関係に直接含まれています:
<dependency>
<groupId>org.tinystruct</groupId>
<artifactId>tinystruct</artifactId>
<version>1.7.26</version>
</dependency>
セキュリティ警告 (プロンプトインジェクション): ツールの戻り値は、AI モデルのコンテキストウィンドウに直接フィードバックされます。ツールの戻り値文字列に呼び出し元から提供された引数を含める前に、それらを必ず検証およびサニタイズする必要があります。入力をサニタイズしないと、攻撃者がモデルの動作を上書きする敵対的な命令 (プロンプトインジェクション) を注入できる可能性があります。常に長さ、文字セット、および null 可能性を検証してください。
MCP ツールを作成するには:
org.tinystruct.mcp.MCPToolを拡張します。- 操作に
@Actionでアノテーションを付け、arguments配列内で@Argumentを使用してパラメータを宣言します。 @Argumentのキーと一致する明示的なメソッド引数としてパラメータを受け入れます。(ツール引数にgetContext().getAttribute(...)を使用しないでください)。
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", "A custom tool for demonstrating MCP");
}
@Action(
value = "custom/hello",
description = "Say hello to someone",
arguments = {
@Argument(key = "name", description = "The name to greet", type = "string", optional = false)
}
)
public String hello(String name) throws MCPException {
// セキュリティ: プロンプトインジェクションの脆弱性を防ぐため、
// モデルに返す前にツール入力を検証/サニタイズします。
if (name == null || name.length() > 50 || !name.matches("^[a-zA-Z0-9 ]+$")) {
throw new MCPException("Invalid name provided");
}
return "Hello, " + name + "!";
}
}
MCP サーバーをデプロイするには:
org.tinystruct.mcp.MCPServerを拡張します。init()をオーバーライドし、this.registerTool()を使用してツールを登録します。フレームワークは自動的に@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";
}
}
ディスパッチャを介してサーバーを実行します:
bin/dispatcher start --import org.tinystruct.system.HttpServer --import com.example.MyMCPServer
設定
設定は src/main/resources/application.properties で管理されます。
# データベース
driver=org.h2.Driver
database.url=jdbc:h2:~/mydb
database.user=sa
database.password=
# サーバー
default.home.page=hello
server.port=8080
# ロケール
default.language=en_US
# セッション (クラスタ環境では Redis)
# default.session.repository=org.tinystruct.http.RedisSessionRepository
# redis.host=127.0.0.1
# redis.port=6379
アプリケーションで設定値にアクセスする:
String port = this.getConfiguration("server.port");
レッドフラグとアンチパターン
| 症状 | 正しいパターン |
|---|---|
com.google.gson または com.fasterxml.jackson をインポートしている | org.tinystruct.data.component.Builder / Builders を使用する。 |
JSON 配列に List<Builder> を使用している | ジェネリック型消去の問題を避けるため Builders を使用する。 |
ApplicationRuntimeException: template not found | API 専用アプリの場合は init() で setTemplateRequired(false) を呼び出す。 |
private メソッドに @Action でアノテーションしている | フレームワークに登録されるためには、アクションは public である必要があります。 |
アプリに main(String[] args) をハードコーディングしている | すべてのモジュールのエントリポイントとして bin/dispatcher を使用する。 |
手動による ActionRegistry の登録 | 自動検出には @Action アノテーションを推奨。 |
| 実行時にアクションが見つからない | クラスが --import でインポートされているか、application.properties にリストされていることを確認する。 |
| CLI 引数が表示されない | --key value で渡す。getContext().getAttribute("--key") でアクセスする。 |
| 同じパスの2つのメソッドがあり、間違った方が起動する | 明示的な mode (例: HTTP_GET と HTTP_POST) を設定して区別する。 |
ベストプラクティス
- 粒度の細かいアプリケーション: 1 つの巨大なクラスではなく、ロジックをより小さく焦点を絞ったアプリケーションに分割します。
init()でのセットアップ: コンストラクタではなく、init()をセットアップ (設定、DB) に活用します。setAction()を呼び出さないでください —@Actionアノテーションを使用します。- モード認識:
@ActionのModeパラメータを使用して、機密性の高い操作をCLIのみまたは特定の HTTP メソッドに制限します。 - パラメータよりコンテキスト: オプションの CLI フラグの場合は、メソッドシグネチャにパラメータを追加する代わりに、
getContext().getAttribute("--flag")を使用します。 - 非同期イベント: イベントによってトリガーされる重いタスクの場合は、イベントハンドラー内で
CompletableFuture.runAsync()を使用します。
テクニカルリファレンス
詳細ガイドは references/ ディレクトリにあります:
- アーキテクチャと設定 — 抽象化、パッケージマップ、プロパティ
- ルーティングと @Action — アノテーションの詳細、モード、パラメータ
- データ処理 — Builder、Builders、JSON シリアライゼーションとパース
- データベース永続化 — AbstractData POJO、CRUD、マッピング XML、POJO 生成
- システムと使用法 — コンテキスト、セッション、SSE、ファイルアップロード、イベント、ネットワーキング
- テストパターン — JUnit 5 のユニットテストと HTTP 統合テスト
リファレンスソースファイル (内部)
src/main/java/org/tinystruct/AbstractApplication.java— ライフサイクルフックを備えたコア基本クラスsrc/main/java/org/tinystruct/system/annotation/Action.java— アノテーションとモードsrc/main/java/org/tinystruct/application/ActionRegistry.java— ルーティングエンジンsrc/main/java/org/tinystruct/data/component/Builder.java— JSON オブジェクトシリアライザsrc/main/java/org/tinystruct/data/component/Builders.java— JSON 配列シリアライザsrc/main/java/org/tinystruct/data/component/AbstractData.java— CRUD を備えた基本 POJO クラスsrc/main/java/org/tinystruct/data/Mapping.java— XML マッピングパーサーsrc/main/java/org/tinystruct/data/tools/MySQLGenerator.java— POJO ジェネレータリファレンスsrc/main/java/org/tinystruct/data/component/FieldType.java— SQL から Java への型マッピングsrc/main/java/org/tinystruct/data/component/Condition.java— 流暢な SQL クエリビルダーsrc/main/java/org/tinystruct/http/SSEPushManager.java— SSE 接続管理src/test/java/org/tinystruct/application/ActionRegistryTest.java— レジストリテストの例src/test/java/org/tinystruct/system/HttpServerHttpModeTest.java— HTTP 統合テストパターン