· design / rfc / release
spec 1.7 リリース — ステートマシン × クラス × シーケンスを 1 つの IR でリンクする
動機 — 図の整合は人間がやるには疲れすぎる
実装と設計の乖離をなくす目的で UML を使うとき、ER 図 / クラス図 / ステートマシン 図 / シーケンス図のうち、特に ステートマシン が他の図と切り離され がちです。
- 「Order の status は
DRAFT → CONFIRMED → SHIPPEDと遷移する」 - 「
confirm()メソッドは draft でしか呼べない (前提条件)」 - 「シーケンス図では
Customer ->> Order: confirm()と書く」
この 3 つの記述は同じ事実を別の図で語り直しているだけですが、UML ツール側がそれを 知らないので、誰かが手で 3 重管理 することになります。コードベースが回り だすと真っ先に乖離する箇所です。
spec 1.7 の答え — 「同じ名前で参照すれば、リンタが繋いでくれる」
バンドルされる 3 つの RFC は別々の機能ですが、組み合わせると 4 ビューの整合が リンタで担保されます。
- RFC 0051 —
@states(initial: ..., final: [...])
状態を駆動する attribute に明示マーカーを付与。stateMachine 図のレンダラと L050 リントが、これを起点に状態遷移を判別する。 - RFC 0050 — fn @pre/@post から状態遷移を自動推論
メソッドに書かれた@pre("status == DRAFT") @post("status == CONFIRMED")から「DRAFT → CONFIRMED はconfirm()がトリガー」と機械的に導く。 sequence のメッセージとも自動で照合される。 - RFC 0052 —
eventを一級宣言に
ドメインイベントをevent OrderConfirmed { ... }と宣言し、fn @emits(OrderConfirmed)と sequence のOrderConfirmedメッセージから同名で参照できる。CQRS / Event Sourcing と相性が良い。
例 — Order の生涯を 1 ファイルで
namespace shop
enum OrderStatus { DRAFT, CONFIRMED, SHIPPED, CANCELLED }
event OrderConfirmed { orderId UUID!; at Timestamp! }
event OrderShipped { orderId UUID!; trackingNo string! }
model Order @aggregate_root {
id UUID! @id
status OrderStatus! @states(initial: DRAFT, final: [SHIPPED, CANCELLED])
fn confirm()
@pre("status == DRAFT")
@post("status == CONFIRMED")
@emits(OrderConfirmed)
fn ship()
@pre("status == CONFIRMED")
@post("status == SHIPPED")
@emits(OrderShipped)
}
view orders-er @er_diagram { include: shop.* }
view order-life @state_machine { include: shop.Order, shop.OrderStatus }
view confirm-flow @sequence_diagram {
participants: Customer as c, Order as o, EventBus as eb
seq {
c ->> o : "confirm()"
o ->> eb : "OrderConfirmed"
}
}state_machine のレンダラは、@states.initial を起点に 状態を並べ替え、fn @pre/@post から導いたトリガー名 (confirm / ship) を矢印に添える。 sequence ではOrderConfirmed のラベルが «event» ステレオタイプ付きでレンダリングされる ── 普通のメソッド呼び出しと視覚的に区別。
新しい lint ルール — L050〜L056
| Code | 検査内容 |
|---|---|
| L050 | state machine 上の遷移に対応する fn @pre/@post がないか |
| L051 | fn @pre/@post で参照される状態名が enum に存在するか |
| L052 | 到達不能な enum 値 (初期状態でも @post 対象でもない) |
| L053 | 状態を持つ model 上で、状態遷移にもシーケンスにも現れない孤立した fn |
| L054 | sequence のメソッド呼び出しが状態を変えていない (整合性違反の疑い) |
| L055 | @emits(X) で参照する event が宣言されているか |
| L056 | 宣言された event がどこからも emit / 参照されていない |
全て draft モードでは info、strict モードで warning〜error に昇格します。既存サンプルや 既存 DSL を破壊しません。
制約と非ゴール
- ガード条件 (
status == X && balance > 0) や UML サブステート (階層的状態) はまだ対象外。L050–L054 は<field> == <STATE>の素朴な等式のみ「状態判別の述語」と扱います。 eventは新たな予約語です。既存 DSL でfn apply(event: ...)のようにeventを識別子として使って いる場合はevt等にリネームしてください。
移行
ほとんどのプロジェクトは @umlay/cli と VS Code 拡張を 0.7.0 に上げる だけで動きます。1.7 で導入された機能は全て opt-in なので、既存の view ... @state_machine サンプルはそのまま動作します。
# CLI (npm)
pnpm add -D @umlay/cli@^0.7.0
umlay check src/**/*.umlay --stats
# VS Code 拡張
code --install-extension keydrop.umlayロードマップの位置付け
1.7 は 「設計の整合性を実装と同期させる」 という北極星に対して、最後の 大きな空白だったステートマシン領域を埋めるリリースです。次は 1.8 — codegen 拡充 (構造化 @@inv から runtime バリデータ自動生成、@@example から property-based test 自動生成) と AI 協働ループの強化 を予定しています。
試すには ブラウザ版エディタ で order-events サンプルを開いてみてください。仕様の正本は GitHub の RFC 0050 / 0051 / 0052 で読めます。