2021年12月31日金曜日

Kaleidox状態機械/エンティティの状態遷移によるアクション

前回は状態機械を持ったエンティティを状態遷移させてみました。

今回は状態機械にアクションを設定し、状態遷移に伴うアクションの動作を確認します。

モデル

モデルは基本的には前回のものと同じですが、各アクションにアクションが実行されたことを示す文字列をコンソールに表示するようにしました。

アクションにはKaleidox言語を設定することができます。そこで、各アクションではKaledioxのprint関数を呼び出すようになっています。

  1. * event  
  2. event=[{  
  3.     name="confirm"  
  4.   },{  
  5.     name="reject"  
  6.   },{  
  7.     name="delivered"  
  8.   },{  
  9.     name="cancel"  
  10.   },{  
  11.     name="suspend"  
  12.   },{  
  13.     name="resume"  
  14. }]  
  15. * entity  
  16. ** salesorder  
  17. *** features  
  18. table=salesorder  
  19. *** attributes  
  20. | Name | Type  | Multiplicity |  
  21. |------+-------+--------------|  
  22. | id   | token |            1 |  
  23. | price| int   |            1 |  
  24. *** statemachines  
  25. **** status  
  26. state=[{  
  27.   name=INIT  
  28.   transition=[{  
  29.     to=running  
  30.     effect="println \"transition from INIT to running\""  
  31.   }]  
  32.   entry="println \"entry INIT\""  
  33.   exit="println \"exit INIT\""  
  34.   do.entry="println \"do.entry INIT\""  
  35.   do.exit="println \"do.exit INIT\""  
  36. },{  
  37.   name=canceled  
  38.   transition=[{  
  39.     to=FINAL  
  40.     effect="println \"transition from canceled to FINAL\""  
  41.   }]  
  42.   entry="println \"entry canceled\""  
  43.   exit="println \"exit canceled\""  
  44.   do.entry="println \"do.entry canceled\""  
  45.   do.exit="println \"do.exit canceled\""  
  46. },{  
  47.   name=suspended  
  48.   transition=[{  
  49.     guard=resume  
  50.     to=HISTORY  
  51.     effect="println \"transition from suspended to HISTORY\""  
  52.   }]  
  53.   entry="println \"entry suspended\""  
  54.   exit="println \"exit suspended\""  
  55.   do.entry="println \"do.entry suspended\""  
  56.   do.exit="println \"do.exit suspended\""  
  57. }]  
  58. statemachine=[{  
  59.   name="running"  
  60.   state=[{  
  61.     name=INIT  
  62.     transition=[{  
  63.       to=applying  
  64.       effect="println \"transition from running.INIT to running.applying\""  
  65.     }]  
  66.     entry="println \"entry running.INIT\""  
  67.     exit="println \"exit running.INIT\""  
  68.     do.entry="println \"do.entry running.INIT\""  
  69.     do.exit="println \"do.exit running.INIT\""  
  70.   },  
  71.   {  
  72.     name=applying  
  73.     transition=[{  
  74.       to=confirming,  
  75.       effect="println \"transition from running.applying to running.confirming\""  
  76.     }]  
  77.     entry="println \"entry running.applying\""  
  78.     exit="println \"exit running.applying\""  
  79.     do.entry="println \"do.entry running.applying\""  
  80.     do.exit="println \"do.exit running.applying\""  
  81.   },{  
  82.     name=confirming  
  83.     transition=[{  
  84.       guard=confirm  
  85.       to=confirmed  
  86.       effect="println \"transition from running.confirming to running.confirmed\""  
  87.     },{  
  88.       guard=reject  
  89.       to=rejected  
  90.       effect="println \"transition from running.confirming to running.rejected\""  
  91.     }]  
  92.     entry="println \"entry running.confirming\""  
  93.     exit="println \"exit running.confirming\""  
  94.     do.entry="println \"do.entry running.confirming\""  
  95.     do.exit="println \"do.exit running.confirming\""  
  96.   },{  
  97.     name=confirmed  
  98.     transition=[{  
  99.       to=delivering  
  100.       effect="println \"transition from running.confirmed to running.delivering\""  
  101.     }]  
  102.     entry="println \"entry running.confirmed\""  
  103.     exit="println \"exit running.confirmed\""  
  104.     do.entry="println \"do.entry running.confirmed\""  
  105.     do.exit="println \"do.exit running.confirmed\""  
  106.   },{  
  107.     name=rejected  
  108.     transition=[{  
  109.       to=FINAL  
  110.       effect="println \"transition from running.rejected to FINAL\""  
  111.     }]  
  112.     entry="println \"entry running.rejected\""  
  113.     exit="println \"exit running.rejected\""  
  114.     do.entry="println \"do.entry running.rejected\""  
  115.     do.exit="println \"do.exit running.rejected\""  
  116.   },{  
  117.    name=delivering  
  118.     transition=[{  
  119.       guard=delivered  
  120.       to=delivered  
  121.       effect="println \"transition from running.delivering to running.delivered\""  
  122.     }]  
  123.     entry="println \"entry running.delivering\""  
  124.     exit="println \"exit running.delivering\""  
  125.     do.entry="println \"do.entry running.delivering\""  
  126.     do.exit="println \"do.exit running.delivering\""  
  127.   },{  
  128.     name=delivered  
  129.     transition=[{  
  130.       to=FINAL  
  131.       effect="println \"transition from running.delivered to FINAL\""  
  132.     }]  
  133.     entry="println \"entry running.delivered\""  
  134.     exit="println \"exit running.delivered\""  
  135.     do.entry="println \"do.entry running.delivered\""  
  136.     do.exit="println \"do.exit running.delivered\""  
  137.   }]  
  138.   transition=[{  
  139.     guard=cancel  
  140.     to=canceled  
  141.     effect="println \"transition from running to canceled\""  
  142.   },{  
  143.     guard=suspend  
  144.     to=suspended  
  145.     effect="println \"transition from running to suspended\""  
  146.   }]  
  147. }]  

イベント

以下の6つのイベントを定義しています。

confirm
確認OK
reject
確認却下
delivered
配送済み
cancel
キャンセル
suspend
保留
resume
再開

前回からの変更点はありません。

エンティティ

エンティティの定義も前回から変更点はありません。

entity節の下にエンティティ「salesorder」を定義しています。

エンティティ「salesorder」の下にfeatures節で特性、attributes節で属性、statemachines節で状態機械を定義しています。

statemachines節の下に状態機械「status」を定義しています。

状態機械

定義したモデルの状態機械図は前回と同じ以下となります。

アクション

状態機械の以下の場所にアクションを定義しました。

  • 状態のentry, exit, do.entry, do.exit
  • 遷移のeffect

アクションはKaleidoxスクリプトでアクションの設定位置をコンソールに表示するものです。

実行

それでは実行してみましょう。

準備

まず、entity-create-collection関数を使ってエンティティを格納する入れ物であるコレクションの作成(内部的にはDBテーブルの作成)を行います。

  1. kaleidox> entity-create-collection 'salesorder  
  2. true  

この処理ではエンティティの状態遷移は起こらないのでコンソールへの表示は行われません。

エンティティの作成

次にentity-create関数でエンティティを作成します。

  1. kaleidox> entity-create 'salesorder price=100  
  2. entry INIT  
  3. do.entry INIT  
  4. do.exit INIT  
  5. exit INIT  
  6. transition from INIT to running  
  7. entry running.INIT  
  8. do.entry running.INIT  
  9. do.exit running.INIT  
  10. exit running.INIT  
  11. transition from running.INIT to running.applying  
  12. entry running.applying  
  13. do.entry running.applying  
  14. do.exit running.applying  
  15. exit running.applying  
  16. transition from running.applying to running.confirming  
  17. entry running.confirming  
  18. do.entry running.confirming  
  19. id:salesorder-67Yh9FdYry41hzuS9WDEtF;price:100;status:confirming  

アクションの設定を行う前だと以下のようになっていたところですが、多数のアクションが実行されたことが分かります。この差分がアクションの動作ということになります。

  1. kaleidox> entity-create 'salesorder price=100  
  2. id:salesorder-67Yh9FdYry41hzuS9WDEtF;price:100;status:confirming  

それぞれ詳しく見ていきます。

オブジェクトが作成されると初期状態INITに入ります。初期状態INITへの進入時に呼ばれるentryアクションとdo.entryアクションが実行されました。

  1. entry INIT  
  2. do.entry INIT  

初期状態INITから最初の状態に自動的に移るので初期状態INITからの退出時に呼ばれるdo.exitアクションとexitアクションが実行されました。

  1. do.exit INIT  
  2. exit INIT  

次に初期状態INITから状態runningへの実際の遷移が行われます。

  1. transition from INIT to running  

状態runningは状態の入れ子になっているので状態機械running内で新たな状態遷移が始まります。このため状態機械runningの初期状態に入り最初の状態applyingに遷移します。状態機械runningの初期状態INITへの進入処理としてentry, do.entry、退出処理としてdo.exit, exitアクションが実行され、初期状態INITから最初の状態applyingに遷移する中でeffectアクションが実行されます。

  1. entry running.INIT  
  2. do.entry running.INIT  
  3. do.exit running.INIT  
  4. exit running.INIT  
  5. transition from running.INIT to running.applying  

状態applyingに入った後は自動的に状態confirmingに移ります。このため状態applyingへの進入処理、退出処理の各アクションが実行され、状態applyingから状態confirmingへの遷移アクションが実行されます。

  1. entry running.applying  
  2. do.entry running.applying  
  3. do.exit running.applying  
  4. exit running.applying  
  5. transition from running.applying to running.confirming  

最後に状態confirmingへの進入時のアクションとしてentry, do.entryアクションが実行され状態confirmingに落ち着きました。

  1. entry running.confirming  
  2. do.entry running.confirming  

オブジェクトの状態を確認するとconfirmingとなっています。

  1. kaleidox> :show:pretty  
  2. ┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓  
  3. ┃Name  │Value                            ┃  
  4. ┣━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫  
  5. ┃id    │salesorder-67Yh9FdYry41hzuS9WDEtF┃  
  6. ┃price │100                              ┃  
  7. ┃status│confirming                       ┃  
  8. ┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛  

エベントの送出

event-call関数でCALLイベントconfirmをsalesorderエンティティ「67Yh9FdYry41hzuS9WDEtF」に対して送出します。

  1. kaleidox> event-call :entity 'salesorder 'confirm "67Yh9FdYry41hzuS9WDEtF"  
  2. do.exit running.confirming  
  3. exit running.confirming  
  4. transition from running.confirming to running.confirmed  
  5. entry running.confirmed  
  6. do.entry running.confirmed  
  7. do.exit running.confirmed  
  8. exit running.confirmed  
  9. transition from running.confirmed to running.delivering  
  10. entry running.delivering  
  11. do.entry running.delivering  
  12. Event[confirm]  

アクションの設定を行う前だと以下のようになっていたところです。この差分がアクションの動作ということになります。

  1. kaleidox> event-call :entity 'salesorder 'confirm "67Yh9FdYry41hzuS9WDEtF"  
  2. Event[confirm]  

それぞれ詳しく見ていきます。

まず状態confirmingから状態confirmedへの遷移が起こります。このため状態confirmingからの退出アクションdo.exit, exitが実行されます。続けて状態confirmingから状態confirmedへの遷移アクションが実行されます。

  1. do.exit running.confirming  
  2. exit running.confirming  
  3. transition from running.confirming to running.confirmed  

状態confirmedから状態deliveringへの遷移は無条件なので自動で遷移が起こります。

状態confirmedへの進入アクションentry, do.entryに続いて退出アクションdo.exit, exitが実行されます。続いて状態confirmedから状態deliveringへの遷移アクションが実行されます。

  1. entry running.confirmed  
  2. do.entry running.confirmed  
  3. do.exit running.confirmed  
  4. exit running.confirmed  
  5. transition from running.confirmed to running.delivering  

最後に状態deliveringへ進入するので状態deliveringのentry, do.entryアクションが実行されます。

  1. entry running.delivering  
  2. do.entry running.delivering  

ここで状態遷移は終わり状態deliveringに落ち着きました。

状態遷移の確認

confirmイベントを受信したsalesorderエンティティの状態をentity-get関数で確認します。

状態機械statusが状態confirmingから状態deliveringに遷移していることを確認できました。

  1. kaleidox> entity-get 'salesorder "67Yh9FdYry41hzuS9WDEtF"  
  2. id:salesorder-67Yh9FdYry41hzuS9WDEtF;price:100;status:delivering  
  3. kaleidox> :show:pretty  
  4. ┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓  
  5. ┃Name  │Value                            ┃  
  6. ┣━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫  
  7. ┃id    │salesorder-67Yh9FdYry41hzuS9WDEtF┃  
  8. ┃price │100                              ┃  
  9. ┃status│delivering                       ┃  
  10. ┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛  

まとめ

今回は状態機械のアクションの動作について確認しました。

状態遷移に伴って、設定したアクションが動作するのでイベント駆動の処理を自然に記述できます。

通常プログラミング言語で状態機械を実装するとそれなりのコード量になり、状態機械モデルとの関係も不明確になりがちですが、モデルそのものにアクションを設定することによりこのような問題が解消されます。

状態機械モデルを直接実行できることはモデル駆動開発の大きなアドバンテージになると思います。

諸元

Kaleidox
0.3.4