· design / rfc / release

spec 1.7 リリース — ステートマシン × クラス × シーケンスを 1 つの IR でリンクする

動機 — 図の整合は人間がやるには疲れすぎる

実装と設計の乖離をなくす目的で UML を使うとき、ER 図 / クラス図 / ステートマシン 図 / シーケンス図のうち、特に ステートマシン が他の図と切り離され がちです。

この 3 つの記述は同じ事実を別の図で語り直しているだけですが、UML ツール側がそれを 知らないので、誰かが手で 3 重管理 することになります。コードベースが回り だすと真っ先に乖離する箇所です。

spec 1.7 の答え — 「同じ名前で参照すれば、リンタが繋いでくれる」

バンドルされる 3 つの RFC は別々の機能ですが、組み合わせると 4 ビューの整合が リンタで担保されます。

  1. RFC 0051 — @states(initial: ..., final: [...])
    状態を駆動する attribute に明示マーカーを付与。stateMachine 図のレンダラと L050 リントが、これを起点に状態遷移を判別する。
  2. RFC 0050 — fn @pre/@post から状態遷移を自動推論
    メソッドに書かれた @pre("status == DRAFT") @post("status == CONFIRMED") から「DRAFT → CONFIRMED は confirm() がトリガー」と機械的に導く。 sequence のメッセージとも自動で照合される。
  3. 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検査内容
L050state machine 上の遷移に対応する fn @pre/@post がないか
L051fn @pre/@post で参照される状態名が enum に存在するか
L052到達不能な enum 値 (初期状態でも @post 対象でもない)
L053状態を持つ model 上で、状態遷移にもシーケンスにも現れない孤立した fn
L054sequence のメソッド呼び出しが状態を変えていない (整合性違反の疑い)
L055@emits(X) で参照する event が宣言されているか
L056宣言された event がどこからも emit / 参照されていない

全て draft モードでは infostrict モードで warningerror に昇格します。既存サンプルや 既存 DSL を破壊しません。

制約と非ゴール

移行

ほとんどのプロジェクトは @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 で読めます。

← ブログトップに戻る