2021年5月31日月曜日

Kaleidox/状態機械

オブジェクト指向分析設計(OOAD)によるソフトウェア開発を進めるための鍵となるのが状態機械です。

Javaなどのオブジェクト指向言語では状態機械は直接サポートはされておらず、状態機械を実現するためのクラスライブラリもよいものがありません。ワークフローエンジンといったものはありますが、仕掛けが大きすぎるので状態機械を実装するために手軽に使用できる感じではないでしょう。

このため、モデリング段階で状態機械を定義しても、プログラミング段階では手組みになってしまうので、モデリングすることの価値がそれほど高くないという状況になっています。また、状態機械をプログラミングで実装するのは案外大変です。

このこともあってか、開発現場では状態機械はほとんど使われていない状況だと思います。

OOADは静的構造モデル(クラス図)と動的モデル(状態機械)が両輪で、その2つのモデルと要求仕様をシナリオ分析であるユースケースモデルで連携させるという仕掛けになっています。

両輪の一つである動的モデルが使用されていないので、当然静的構造モデルと動的モデル、要求仕様を連携させるユースケースモデルも実力を発揮できない状況にあるといえます。

結果として、OOADではなくクラス図を用いたデータ中心設計になってしまっているのが現状ではないでしょうか。

クラウドアプリケーションでは、今後ますますイベント駆動型の応用が重要になってくると予想されますが、この核となるモデルが状態機械であり、状態機械がうまく活用されていない現状はかなり危機的な状況ではないかと思います。

SimpleModelingではこの問題に対処するため、モデルコンパイラSimpleModelerとアクション言語Kaleidoxで状態機械をサポートします。モデリング段階で状態機械モデルを定義することで、状態機械のプログラミングをせずに、アプリケーションでそのまま状態機械を利用できることになります。

今回はKaleidoxで状態機械を使用してみます。

モデル

Kaleidoxで状態機械を動かしてみましょう。

まず状態機械に使用するイベントを定義した後、状態機械のモデルを定義します。

商品販売を単純化したモデルにしてみました。本来は、注文毎に状態機械を持つことになりますが、ここでは簡単化のためグローバルな状態機械をグローバルなイベントで駆動することにします。

* event
event=[{
    name="confirm"
  },{
    name="reject"
  },{
    name="delivered"
}]
* statemachine
statemachine={
  name="purchase"
  state=[{
    name=applying
    transition=[{
      to=confirming
    }]
  },{
    name=confirming
    transition=[{
      guard=confirm
      to=confirmed
    },{
      guard=reject
      to=rejected
    }]
  },{
    name=confirmed
    transition=[{
      to=delivering
    }]
  },{
    name=rejected
    transition=[{
      to=final
    }]
  },{
    name=delivering
    transition=[{
      guard=delivered
      to=delivered
    }]
  },{
    name=delivered
    transition=[{
      to=final
    }]
  }]
}

イベント

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

confirm
確認OK
reject
確認却下
delivered
配送済み

状態機械

定義したモデルを状態機械図で記述すると以下になります。

実行

それでは実行してみます。

まずstatemachine-new関数で状態機械を生成します。生成する状態機械のモデル名はpurchaseです。

生成した状態機械をsetq関数で変数smに束縛します。

状態機械の名前はpurchaseです。状態機械の状態はconfirmingになっています。

kaleidox> statemachine-new 'purchase
StateMachine[purchase:confirming]
kaleidox> setq sm
StateMachine[purchase:confirming]

次にevent-issue関数でconfirmイベントを発行します。

confirmイベント発行の結果、状態機械purchaseの状態はdeliveringになりました。

kaleidox> event-issue 'confirm
Event[confirm]
kaleidox> sm
StateMachine[purchase:delivering]

次にevent-issue関数でconfirmイベントを発行します。

confirmイベント発行の結果、状態機械purchaseの状態はdeliveringになりました。

kaleidox> event-issue 'delivered
Event[delivered]
kaleidox> sm
StateMachine[purchase:final]

これで状態機械の状態遷移は終了です。

状態機械の遷移の履歴をhistoryメソッドで取得することができます。

kaleidox> sm.history
((Event[system.control.init] . State[applying]) (Event[system.control.go-ahead] . State[confirming]) (Event[confirm] . State[confirmed]) (Event[system.control.go-ahead] . State[delivering]) (Event[delivered] . State[delivered]) (Event[system.control.go-ahead] . State[final]))

historyメソッドで取得した結果を元に状態遷移の経緯をまとめると以下になります。

イベント遷移先
system.control.initapplying
system.control.go-aheadconfirming
confirmconfirmed
system.control.go-aheaddelivering
delivereddelivered
system.control.go-aheadfinal

まず、初期状態から自動的に先頭の状態であるapplyingに遷移します。イベントは初期化を示す疑似イベントであるsystem.control.initです。

状態applyingと状態confirming間にはガードがないので状態間を自動的に遷移します。イベントは自動遷移を示す疑似イベントであるsystem.control.go-aheadです。状態機械モデルですが、アクティビティモデル的な振舞いになります。

ここでconfirming状態となります。

次にevent-issue関数でconfirmイベントを発行すると、confirming状態からconfirmed状態に遷移します。

状態confirmedと状態delivering間にはガードがないので状態間を自動的に遷移します。

ここでdelivering状態となります。

次にevent-issue関数でdeliveredイベントを発行すると、delivering状態からdelivered状態に遷移します。

状態deliveredと最終状態final間にはガードがないので状態間を自動的に遷移し、状態機械purchaseは終了します。

reject

続けてrejectイベントのルートも確認しておきます。

statemachine-new関数で状態機械purchaseを生成した直後はconfirming状態になっています。

ここでrejectイベントを発行するとモデルの定義どおり最終状態finalに遷移しました。

kaleidox> statemachine-new 'purchase
StateMachine[purchase:confirming]
kaleidox> setq sm
StateMachine[purchase:confirming]
kaleidox> event-issue 'reject
Event[reject]
kaleidox> sm
StateMachine[purchase:final]

状態機械purchaseはrejectイベントを受信することで状態rejectedに遷移しますが、状態rejectedから最終状態finalの間にガードがないので最終状態finalに自動遷移しています。

まとめ

Kaleidoxで状態機械モデルの実行を行うことができるようになりました。

今回は最もシンプルなモデルですが、このレベルのモデルでもプログラミングレスで使用できるとかなり便利だと思います。

状態機械モデルはもっと複雑な記述が可能で、より広範囲な応用に適用することができます。次回以降、状態機械モデルの色々な使い方について見ていく予定です。

諸元

Kaleidox
0.2.1

2021年4月30日金曜日

Kaleidox/Service

前回はソフトウェア開発の中軸モデル要素であるコンポーネントのメタモデルについて検討しました。

今回はコンポーネントの派生モデル要素であるサービスについて取り上げます。

サービスは外部に対して公開されたコンポーネントという位置付けなので、外部から直接実行できるコンポーネントということができます。

Kaleidoxでサービスモデルを直接実行することができるので、サービスモデルを使ってコンポーネントの特性を見ていきたい思います。

今回はサービスのAPIを定義して、Kaleidox上で実行してみます。

サービスモデル

足し算と引き算を行うサービスcalcを以下のように定義しました。この定義に沿って、サービスモデル(コンポーネントモデル)の定義について説明します。

* Service
** calc
*** Operation
**** plus
***** In
| 名前 | 型  | 多重度 |
|------+-----+--------|
| arg1 | int |      1 |
| arg2 | int |      1 |
***** Out
| 型  | 多重度 |
|-----+--------|
| int |      1 |
***** Method
+ arg1 arg2
**** minus
***** In
| 名前 | 型  | 多重度 |
|------+-----+--------|
| arg1 | int |      1 |
| arg2 | int |      1 |
***** Out
| 型  | 多重度 |
|-----+--------|
| int |      1 |
***** Method
****** js
arg1 - arg2

全体構造

サービスモデルの全体構造は以下のようになっています。

* Service
** サービス名
*** Operation
**** オペレーション名
***** In
***** Out
***** Method

Service節でサービスモデルの定義を行います。

Service節の直下にはサービス名を節名としたサービスの定義が並びます。

サービス名節の直下にはOperation節があります。

Operation節の直下にはオペレーション名を節名としたオペレーションの定義が並びます。

オペレーションの定義は次節で説明します。

拡張予定

前回、コンポーネントの部品として検討した部品の中でAPIに該当する部品をサポートしています。

以下の部品は現在は未実装です。

  • SPI
  • Bus
  • StateMachine
  • Entity
  • Configuration
  • Rule
  • Log
  • Metrics
  • Audit trails

これらのモデル要素はkaleidox上で順次実現していく予定です。

オペレーション

plusオペレーションの定義は以下になります。

*** Operation
**** plus
***** In
| 名前 | 型  | 多重度 |
|------+-----+--------|
| arg1 | int |      1 |
| arg2 | int |      1 |
***** Out
| 型  | 多重度 |
|-----+--------|
| int |      1 |
***** Method
+ arg1 arg2

オペレーションの定義は以下の3つです。

In
入力定義
Out
出力定義
Method
Method定義

In節には入力情報を表形式で記述します。表では入力パラメタの名前、型、多重度を定義します。

Out節には出力情報を表敬式で記述します。表では出力パラメタの型、多重度を記述します。

Method節にはオペレーションの実装であるメソッドの定義を行います。

メソッド

Method節では、オペレーションの実装であるメソッドの定義を行います。

モデルが仕様のみで実装まで定義しない場合はMethod節を省略することができます。

ただし、Method節を省略した場合はKaleidoxでの直接実行はできません。手動で作成したプログラムを、SimpleModelerのプログラム自動生成と組み合わせて使用する形になります。

Kaleidox

オペレーションの実装をKaleidoxスクリプトで定義する場合は、Method節に直接Kaleidoxスクリプトを記述します。

サービスモデルのplusオペレーションの下には、以下のMethod節を定義しています。

***** Method
+ arg1 arg2

calcサービスのplusオペレーションが呼び出されると、Kalidoxスクリプト「+ arg1 arg2」が実行され、その結果が返却されます。

JavaScript

Method節の下にjs節が配置されている場合は、js節内をMethod実現のためのJavaScriptスクリプトとして使用します。

***** Method
****** js
arg1 - arg2

calcサービスのminusオペレーションが呼び出されると、JavaScriptスクリプト「arg1 - arg2」が実行され、その結果が返却されます。

Java ScriptEngine

Method節の下には、JavaScript以外にもJava ScriptEngineでサポートしているスクリプト言語を使用することができます。この場合は、Java ScriptEngineの認識する言語名を節名で指定します。

拡張予定

メソッドの実現方法として、外部のJava/Scalaプログラムを利用可能にする予定です。以下の形式が候補となっています。

  • JavaBeans (JAR)
  • Servlet (WAR)
  • OSGi

実行

それでは、Kaleidoxでサービスモデルを直接実行してみます。

前述のサービスモデルをinit.kldに格納してkaleidoxが認識できるようにします。

コマンド

まず、コマンドラインで実行してみます。

$ klaidox calc.plus 1 2
3

サービス名とオペレーション名を「.」で繋いだ文字列でオペレーション名を指定します。calcサービスのplusオペレーションなので「calc.plus」となります。

引数には「1」と「2」を指定した結果、計算結果の3が返されました。

REPL

次はKaleidoxのREPLで実行してみます。

REPLのプロンプトから「calc.plus 1 2」と実行すると、計算結果の3が返されました。

kaleidox> calc.plus 1 2
3

トレース

calc.plusの実行時のコールツリーは、REPLコマンド「:trace」で表示することができます。先程の「calc.plus 1 2」の場合は以下になります。

kaleidox> :trace
INVOKE(302)[Script] (calc.plus 1 2) => 3
  INVOKE(104)[Function(Operation):service-script-kaleidox(calc.plus)] (calc.plus 1 2) => 3
    INVOKE(60)[Lambda:calc.plus] (1 2) => 3
      INVOKE(53)[Script] (+ arg1 arg2) => 3
        INVOKE(23)[Function(Eval):+] (+ arg1 arg2) => 3
          INVOKE(15)[Variable:arg1] arg1 => 1
          INVOKE(1)[Variable:arg2] arg2 => 2

Web REST

KaleidoxをWebで実行するためのコンテナとしてpbjを開発しました。

  • https://github.com/asami/pbj

pbjを実行するとWebサーバーが立ち上がります。ポート番号は8080です。

$ pbj

curlコマンドでpbjサーバーを呼び出します。

パス名はサービス名「calc」、オペレーション名「plus」をつなげた「/calc/plus」です。引数にはarg1、arg2にそれぞれ「1」、「2」を指定します。

$ curl "http://localhost:8080/calc/plus?arg1=1&arg2=2"
{"status":200,"data":3}

実行の結果、JSONの形式で実行結果が返されました。プロパティdataに計算結果の「3」が入っています。

jqコマンドを併用してJSONを整形すると、以下のようになります。

$ curl "http://localhost:8080/calc/plus?arg1=1&arg2=2" | jq .
{
  "status": 200,
  "data": 3
}

トレース

Webサーバ上で実行したときのコールツリーは「_trace」パラメタを「true」にすると取得することができます。

$ curl "http://localhost:8080/calc/plus?arg1=1&arg2=2&_trace=true"  | jq
{
  "status": 200,
  "data": 3,
  "trace": {
    "kind": "root",
    "trace": [
      {
        "kind": "invoke",
        "timestamp": "1619340084576",
        "label": "Script",
        "enter": "(calc.plus :arg1 1 :arg2 2)",
        "leave": "3",
        "lap": 8,
        "trace": [
          {
            "kind": "invoke",
            "timestamp": "1619340084577",
            "label": "Function(Operation):service-script-kaleidox(calc.plus)",
            "enter": "(calc.plus :arg1 1 :arg2 2)",
            "leave": "3",
            "lap": 6,
            "trace": [
              {
                "kind": "invoke",
                "timestamp": "1619340084578",
                "label": "Lambda:calc.plus",
                "enter": "(1 2)",
                "leave": "3",
                "lap": 5,
                "trace": [
                  {
                    "kind": "invoke",
                    "timestamp": "1619340084581",
                    "label": "Script",
                    "enter": "(+ arg1 arg2)",
                    "leave": "3",
                    "lap": 2,
                    "trace": [
                      {
                        "kind": "invoke",
                        "timestamp": "1619340084582",
                        "label": "Function(Eval):+",
                        "enter": "(+ arg1 arg2)",
                        "leave": "3",
                        "lap": 1,
                        "trace": [
                          {
                            "kind": "invoke",
                            "timestamp": "1619340084582",
                            "label": "Variable:arg1",
                            "enter": "arg1",
                            "leave": "1",
                            "lap": 0
                          },
                          {
                            "kind": "invoke",
                            "timestamp": "1619340084582",
                            "label": "Variable:arg2",
                            "enter": "arg2",
                            "leave": "2",
                            "lap": 1
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
}

まとめ

サービスのモデルを作成し、Kaleidoxで直接実行してみました。今回はAPIのみでしたが、コンポーネントの回に検討したBusなどのメカニズムとそのモデルについても順次実現していく予定です。

同じモデルがコマンドライン、REPL、Webのそれぞれでそのまま実行できることを確認できました。モデルを直接実行できれば、プログラミングの工数削減、仕様と実装のズレの低減といった大きなメリットがあります。

また、Kaleidoxのようなモデル実行プラットフォームが提供するアプリケーションフレームワーク機能により、ドメインモデルではスコープ外の非機能要件についても自動的に利用可能になるメリットもあります。

今回はKaleidoxが提供する機能であるトレース機能を紹介しました。実運用では、本番環境でしか発生しない障害などが発生することが多々ありますが、トレース機能を用いると効率的に障害調査を行うことができることを期待できます。

諸元

Kaleidox
0.1.16
Pbj
0.0.2

2021年3月31日水曜日

SimpleModeling/Component

SimpleModelingでは、コンポーネントをソフトウェア開発を行う上での中軸となるモデル要素と捉えています。今回はコンポーネントのメタモデルを検討します。

コンポーネントはオブジェクトやサービス、システムと同様にメタモデル上は分類子(classifier)を親としており分類子の構造を引き継いでいます。コンポーネントのメタモデルの検討を行うのはコンポーネントのメタモデルを軸に、上方向ではサービスやシステム、下方向ではオブジェクトのメタモデルを考える上でのベースラインにする狙いもあります。

Componentの構造

SimpleModelingではComponentの構造を以下の図に示すメタモデルをベースにメタモデルの検討を行っています。

以下でそれぞれのモデル要素についてみていきます。

API

本案では以下の3つのAPIを定義しました。

  • Service API
  • Management API
  • Resource API

APIではOperation群を定義します。

Operationの実装はプログラム言語のメソッドが基本で、必要に応じてRESTを含むRPCとして実装します。

Service API

コンポーネント利用者向けのAPIです。

Webなどフロントエンド・アプリケーションやその延長線上にある他コンポーネントからの利用を想定しています。

Management API

コンポーネントの提供する機能やコンポーネントが管理するデータの運用管理をするためのAPIです。

管理コンソールやその延長線上にある他コンポーネントからの利用を想定しています。

Resource API

リソース操作用のAPIです。

リソース操作用のAPIは個々のOperationの責務は前述のService API、Management APIに分類されるため分類上の重複がありますが、モデルからの自動生成の対象なので図では独立した形で記述しています。

SPI

SPI(Service Provider Interface)はComponentに対してサービスを影響するコンポーネントやオブジェクトを接続するためのインタフェースです。

たとえば、ECシステムで外部の決済サービスを利用する場合、決済サービスを呼び出しすためのドライバを作成しSPIを通じてコンポーネントと接続します。

SPIを経由して接続する外付けのプログラムでComponentの機能を拡張していくことができます。このため、SPIはComponentの拡張点(Extension Point)ということができます。

Bus

クラウド・アプリケーションを開発する上で重要な構成要素がBusです。クラウド・アプリケーションはクラウド内で発生する各種イベントに即応するためイベント駆動型の振舞いが極めて重要です。単純にAPIが提供するOperationを呼び出すだけの処理ではまかないきれなくなっているわけです。

このクラウドワイドなイベント駆動処理の基盤となるのがBusです。

SimpleModelingではBusを第一級の構成要素としてメタモデル上での扱いを明確にし、SimpleModelerやKaleidoxといったツールでもBusの使用を前提とした機能セットを提供する予定です。

Busには以下の示す特性があります。

  • イベント駆動
  • 透過性
  • 疎結合
  • 非同期
  • スケーラビリティ
  • 可用性(availablity)

これらの特性はクラウド・アプリケーションを構築する上で重要な役割を担います。

Busも用途に応じていくつかの種類が考えられます。図では以下のBusを示しています。

  • Service Bus
  • Inter-Component Bus
  • Async Bus
  • Sync Bus

それぞれ見ていきましょう。

Service Bus

ネットワークワイドでコンポーネント間の同報通知を行うバスです。

Apache KafkaやAWS SNS、AWS Kinesis、AWS EventBridgeといったサービスの利用を想定しています。

基本のBusとなります。Service Busを使うことでComponent内のObject間通信もインターネットワイドなサービス間の通信も透過的に行うことができるので、理想的にはService Busのみを使用するのが望ましいといえます。

ただ、現実のアプリケーションではさまざまなニーズが存在するので、それらのニーズをすくい取るためにメタモデルでは用途に応じたBusを定義しています。

Inter-Component Bus

同一プロセス内でComponentを接続するバスです。

通常のOperationに対してBusを使う上での問題点として以下のものがあります。

  • 非同期になってしまう。
  • データベース・トランザクションの制御から外れてしまう。

非同期の問題点はエラー情報をその場ではクライアントに伝えることができなくなることです。このため大掛かりなエラー処理の仕掛けを作る必要が出てくるのでアプリケーション開発の複雑度が一気に増加します。

いうまでもなくデータベース・トランザクションから外れてしまう問題もエラー処理の複雑度が大きく増加します。

もちろんBusの提供するイベント駆動、透過性、疎結合の特性は魅力的です。

そこで同一プロセス内で動くComponentやObject間の協業では、本来同期型&トランザクションは可能なはずなので、イベント駆動、透過性、疎結合の特性を持ちつつ、同期型&トランザクションのサポートを行うBusとしてInter-Component Busを用意しました。

Async Bus と Sync Bus

Component内部で使用するBusです。このメタモデルでは以下の2つに分けています。

  • Async Bus
  • Sync Bus

いずれもComponent内に閉じていることにより効率的な実行を想定しています。

Async Busはシグナルの同報処理を非同期で行うバスです。Service Busと同じ特性を持ちます。

Sync Busははシグナルの同報処理を同期で行うバスです。Inter-Component Busと同じ特性を持ちます。

Async BusとSync Busは、プラットフォームによってはそれぞれService Bus、Inter-Component Busを直接使用する方式でよいかもしれません。

Object

Component内には用途ごとに各種オブジェクトが存在することになります。

図では以下のObjectを図示しています。

  • Service API, Management API, Resource APIを司るObject
  • SPIを司るObject
  • Bus(Async Bus, Sync Bus)に接続したObject
  • Entity
  • 図の中央にあり各Objectを中継しているObject

StateMachine

Objectのメタモデルを定義する上で重要と考えているのがState Machineです。本来オブジェクト指向の動的モデルはState Machineを使ってモデル化するのが筋ですが、既存のプログラミング言語に該当する機能がないこともあり、一般のエンタープライズやWebアプリケーションでは現状はほとんど利用されることがないと思います。

しかし、イベント駆動が重要な位置を占めるクラウド・アプリケーションではStateMachineが動的モデルの要のモデルです。このためSimpleModeingではStateMachineをメタモデルで定義し、SimpleModelerやKaleidoxといったツールで直接使用できるようにする予定です。

Entity

データベースに格納して管理するPersistent ObjectをSimpleModelingではEntityと呼びます。

SimpleModelingではEntityはデータベース内にあるため直接操作はせず、一旦メモリ内のObjectに転記し、転記したObjectで状態を更新した上ででデータベース上のEntityに書き戻すと、という操作モデルを取ります。

メモリ内のObjectに転記する場合に、必ずしもデータベース上のEntityのクローンである必要はなく、アプリケーションのモデルに応じたメモリ内のObjectに必要な情報を転記することになります。

EntityはStateMachineを持つことができます。実装的には、Entityをメモリ上に展開したObjectにStateMachineを定義し、そのStateMachineが動作するという形になります。

設定

Componentの振舞いをComponentの想定した範囲で変えるための変化点(Variation Point)のメカニズムとして以下の2つを用意しました。

  • Configuration
  • Rule

Configuration

ロケールやタイムゾーンといったアプリケーションの国際化情報やタイムアウト時間など、Componentの基本的な振舞いに対するパラメタ設定を行います。。

格納場所は配備時にファイルを添付したり、外部のリポジトリに格納といった手段が考えられます。

Rule

アプリケーションがモデル化したRuleを、アプリケーションのユースケースに合わせて設定します。

消費税率といったパラメタ的なものから、エキスパート型AIの推論ルールのようなものまでを想定しています。

記録

運用上の様々な要件からコンポーネントの動作記録を取っておく必要があります。

  • Log
  • Metrics
  • Audit trail

Log

動作ログです。

アプリケーションの各種メトリックスの計算、デバッグ情報、性能改善の基礎データ、課金情報として用いることを想定しています。

上記の用途に必要な情報を採取できるよう、ログとして採取する情報についても分析・設計が必要です。

Audit trail

セキュリティの監査記録です。セキュリティ監査のための基本情報としてユーザーがComponentをどのように使ったか、どのリソースを参照・更新したかの記録を取ります。

Logよりも厳密な情報を採取する必要があるので、モデルを分けています。

Metrics

コンポーネントの振る舞いをデータ化したメトリックス情報です。オペレーションの呼び出し回数やキャッシュのヒット率といった情報を記述します。

性能改善の基礎データや課金情報として用いることを想定しています。

まとめ

今回はSimpleModelingで重要視しているモデル要素であるComponentについてメタモデルを検討しました。

次回はこのメタモデルをベースに、ComponentをKaleidoxで扱う方法について考えてみます。

2021年2月28日日曜日

Kaleidox/データ型

アプリケーション開発をモデル駆動で行う時に、見過ごしがちですが案外重要なのがデータ型の扱いです。

データ型の標準化

一般的にプログラミング言語では数値や文字列を中心に必要最小限のデータ型を定義し、アプリケーションで必要なデータ型はクラスライブラリで定義して使用するという建付けになっています。

UMLを使用したモデリングも事情は同じで、アプリケーションで必要なデータ型を定義して使用することになります。プログラミング言語と違ってクラスライブラリ的な共通モジュールはあまり提供されていないので、データ型の問題はより深刻といえます。

まだモデル駆動開発という観点からは、モデリング側のデータ型とプログラミング言語側のデータ型の連携も問題となります。

このためアプリケーション開発を始めるにあたっては、アプリケーション開発者側で以下の定義が必要になります。

  • モデリングでのデータ型
  • プログラミング言語でのデータ型
  • モデリングのデータ型とプログラミング言語のデータ型のマッピング

エンタープライズアプリケーションは最初に小さなWebシステムを作るだけであっても、次々と追加のアプリケーションを開発し、それらを連携させて動作させることになりがちです。このような場合、アプリケーションごとに独自のデータ型を定義指定使っていては連携に支障が出てくる可能性が高いでしょう。

リテラル問題

データ型の標準化を行う際には、データ型の文字列表現であるリテラルの標準化も重要な問題です。

モデリング、プログラミング、データ入出力の各フェーズでデータの表現形式が異なるのは非効率ですし、トラブルの元になります。

移入、移出するデータフォーマットの統一という観点からもリテラルの標準化、共通化が重要です。

国際化

アプリケーション開発で、分かっていても開発期間や工数の関係から後回しになりがちなのが国際化(I18N, L10N)です。

直近では使わないかもしれないけれど、海外展開する時には必要になる、という優先度の見えにくい機能なのでアプリケーションを普通に開発すると自然と国際化対応できているという形が理想的です。

ここで鍵になるのがデータ型です。時間や文字列などで国際化を意識したデータ型を用いることで、特別な意識なしに開発したアプリケーションを国際化対応にすることが可能になります。

SimpleModelingでの解決策

データ型の問題に対しては、上流のモデリングからプログラミングまで共通のデータ型を共通化するのが有力な作戦です。

本Blogで進めているSimpleModelingでは以下のアプローチで対応しています。

  • Action言語Kaleidoxで、アプリケーション向けのデータ型を定義する
  • データ型はできるだけ文字列リテラルで記述可能にする
  • SimpleModelerでのモデリングでKaleidoxで定義したデータ型を使えるようにする
  • 移入、移出するデータフォーマットもデータ型のリテラルを扱えるようにする

実行例

データ型をリテラルで指定する例です。

準備

準備として以下のinit.kldを用意します。

schema区画でスキーマpersonを定義しています。

data-store区画でデータベースのテーブルpersonに移入するデータを定義しています。

* env

db.default.driver="org.h2.Driver"
db.default.url="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false"

* schema

** person

| 特性 | 名前          | 型       | 多重度 | ラベル  |
|------+---------------+----------+--------+---------|
| 属性 | id            | int      | 1      | User ID |
| 属性 | name          | string   | 1      | 名前    |
| 属性 | city          | string   | ?      | 市      |
| 属性 | age           | int      | ?      | 年齢    |
| 属性 | registered_at | datetime | ?      | 登録日  |

* data-store

** person

100,Taro,Yokohama,28,2021-01-10T00:00:00
200,Hanako,Kawasaki,24,2021-02-20T00:00:00

データ定義の際に「2021-01-10T00:00:00」という形で日時をリテラルで記述しています。これはローカル日時を記述するデータ型localdatetime型のデータとなります。

データベース移入時には、実行環境のタイムゾーンを補完したdatetime型のデータに自動的にマッピングされます。

使用例

上記のinit.kldに初期化の結果、インメモリデータベースのpersonテーブルにデータが2件移入されています。

store-select関数で取得すると以下になります。

kaleidox> store-select 'person
Table[5x2]
kaleidox> :show
Table[5x2]
┏━━━┯━━━━━━┯━━━━━━━━┯━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃id │name  │city    │age│registered_at            ┃
┣━━━┿━━━━━━┿━━━━━━━━┿━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃100│Taro  │Yokohama│35 │2021-01-10T00:00:00+09:00┃
┃200│Hanako│Kawasaki│24 │2021-02-20T00:00:00+09:00┃
┗━━━┷━━━━━━┷━━━━━━━━┷━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━┛

このテーブルに対して30代の会員の検索を行います。

「30代」を30以上、39以下と考えるとinterval型のリテラル「30~39」で記述することができます。

このリテラルを検索条件に指定すると、目的通りの結果が出力されました。

kaleidox> store-select 'person age=30~39
Table[5x1]
kaleidox> :show
Table[5x1]
┏━━━┯━━━━┯━━━━━━━━┯━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃id │name│city    │age│registered_at            ┃
┣━━━┿━━━━┿━━━━━━━━┿━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃100│Taro│Yokohama│35 │2021-01-10T00:00:00+09:00┃
┗━━━┷━━━━┷━━━━━━━━┷━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━┛

次に2021年2月1日以降の入会者の検索はを行います。

「2021-02-01~」はlocaldatetimeinterval型として解釈されます。

データベース検索時にはタイムゾーンの補完が必要ですが、これはKaleidoxの実行コンテキストによって補完されます。

kaleidox> store-select 'regestered_at=2021-02-01~
Table[5x1]
kaleidox> :show
Table[5x1]
┏━━━┯━━━━━━┯━━━━━━━━┯━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃id │name  │city    │age│registered_at            ┃
┣━━━┿━━━━━━┿━━━━━━━━┿━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃200│Hanako│Kawasaki│24 │2021-02-20T00:00:00+09:00┃
┗━━━┷━━━━━━┷━━━━━━━━┷━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━┛

このリテラルを検索条件に指定すると、目的通りの結果が出力されました。

RESTサーバーでの指定

将来KaleidoxをRESTサーバー化した場合は以下のような指定方法になる想定です。

curl https://example.com/person?age=30~39

リテラル「30~39」という表現でデータ型intervalのデータを、モデリング段階の表記と同じ表記で記述できています。

SimpleModelerとの関係

init.kldのschema区画でデータのスキーマを定義していました。このスキーマでデータ型を指定していますが、このデータ型はSimpleModelerと共通のものになります。

SimpleModelerでのモデル定義からKaleidoxでのスキーマ定義へ、データ型がシームレスに引き継がれます。

まとめ

モデル駆動開発の重要な要素技術であるデータ型に対して、モデリングからプログラミング、データ記述までデータ型、リテラルを共通化するアプローチについて説明しました。

Kaleidoxを中心にSimpleModelerと共通のデータ型、リテラルを使用することでモデリングからプログラミング、運用までシームレスにデータ型を連携させることができます。

標準で共通化できるデータ型を十分に用意できれば、非常に強力なアプローチになると思います。アプリケーション開発に必要なデータ型については必要に応じて拡張していく予定です。

諸元

  • Kaleidox : 0.1.14

データ型一覧

現時点でKaleidoxで定義しているデータ型一覧です。

データ型説明
booleanBool値
shortShort型整数
intInt型整数
longLong型整数
floatFloat型浮動小数点数
doubleDouble型浮動小数点数
integer整数
decimal10進固定小数点数
rational有理数
complex複素数
rangeレンジ
intervalインターバル
string文字列
binaryバイナリ
i18nstring国際化文字列
i18ntemplate国際化テンプレート
regex正規表現
clobCLOB
blobBLOB
slipスリップ(伝票)
schemaスキーマ
queryクエリ
recordレコード
tableテーブル
vectorベクター
matrixマトリックス
dataframeデータフレーム
lxsvLXSV
urlURL
urnURN
uriURI
expression
scriptスクリプト
xmlXML
htmlHTML
xpathXPath
xslXSL
pugPUG
jsonJSON
datetime日時(タイムゾーン付き)
localdatetimeローカル日時
localdateローカル日
localtimeローカル時
montyday月日
datetimeinterval日時インターバル
localdatetimeintervalローカル日時インターバル
duration経過時間
periodピリオド
money金額
percentパーセント
unit単位

2021年1月31日日曜日

SimpleModeling 2021

Modegramming Styleブログではモデリングとプログラミングの一体化を指向したModegrammingというコンセプトを提唱しており、その実現のための技術体系としてSimpleModelingを整備しています。

整備活動

2019年5月に以下の記事でSimpleModelingによるモデル駆動開発の活動をリブートしました。

整備活動の内容はプロダクトの開発とメタモデルの整備に分けられます。

プロダクト

SimpleModelingでは以下の4つのプロダクトの開発を進めています。いずれもオープンソースです。

また商用製品としてボクがCTOをやっているEverforth社でクラウドプラットフォームであるPrefer Cloud Platform(以下PCP)を開発しています。

Smartdox

SmartDoxは以前開発していたSmartDocの後継となる汎用の文書処理系です。

SmartDocはXMLをメタ言語としていましたが、SmartDoxは独自のプレインテキスト(MarkDownとEmacs orgから派生)をメタ言語としています。

SimpleModelingではLiterate Modeilingのアプローチを取っているので、モデルの中の自然言語記述部分の取扱いを重要視しています。

この部分の中核技術となるのがSmartDoxというわけです。

後述するSimpleModelerやKaleidoxはSmartDoxをメタ言語として使用し、この上に固有のメタモデルを構築しています。

SimpleModeler

SimpleModelerはSimpleModelのモデルからプログラムなどの成果物を生成するモデルコンパイラです。

SmartDoxをメタ言語として記述し、SimpleModelメタモデルのインスタンスとして記述したモデル部の情報から各種成果物を生成します。

以前開発していたXMLスキーマコンパイラである Relaxer によるプログラムの自動生成が極めて有効だったことを受けて、プログラムの自動生成のターゲットをオブジェクトモデルに広げるために新規に開発したのがSimpleModelerです。

SimpleModelerは基本機能は動作済みで後述のPCPの開発にも適用しています。

現在、後述するようにSimpleModelingのメタモデルの刷新を行っています。SimpleModelerは、この新メタモデルへの対応を行う予定です。

Kaleidox

Kaleidoxはオブジェクトモデリングとプログラミングをシームレスに連携させることを目的とするアクション言語です。

Kaleidoxの開発を進めながら以下の記事を書いてきました。

Kaleidoxはかなりよい感じで開発が進んでおり、プログラム開発のテスト環境としてボクが日常的に使用するツールになっています。

今年は、SimpleModeler統合を行うことで、モデル駆動開発のアクション言語として本格的に活用できるようにする予定です。

Arcadia

クラウド・アプリケーションでは、サーバーサイドのRESTサービスを中心に、Web、スマートフォン(iOS, Android)、デスクトップの3種類のUIフロントエンドの構成になります。

この中でWebフロントエンドのためのWebフレームワークとして開発中なのがArcadiaです。

モデルコンパイラが扱うオブジェクトモデルとWebアプリケーションの間はインピーダンスミスマッチが大きく、その間を埋めるミドルウェアの存在が重要になってきます。

Webアプリケーション独特の振る舞いや特性を吸収して、モデル駆動開発したアプリケーションロジックとWebでの実装をスムースに連携させることを目的としています。

Webアプリケーションも、大きくWebサーバー上での実行を中心としてWebページの遷移で振る舞いを構成する伝統的な方式(以下Webページ方式)とJavaScriptフレームワークを中心にGUIアプリケーション的な振る舞いを行う方式(以下JavaScriptフレームワーク方式)の2方式に分けることができ、現実的には両者の式の折衷案の方式(以下Web/JavaScript折衷方式)が使用されるケースが多いでしょう。

ArcaidaではWebページ方式をサポートするとともに、JavaScriptフレームワーク方式で必要になるJavaScriptベースのWebフレームワークの実行コンテナとしての機能を提供し、合わせてWeb/JavaScript折衷方式に対応していくというアプローチを取っています。

SimpleModelerから生成されるコードに対して、HTMLページのデザインを行えばアプリケーションが完成するエコシステムの構築を目指しています。

Arcaidaは基本部は開発済みです。Kaleidox、SimpleModelerを組み込んでモデル駆動対応を達成できた後に公開する予定です。

Prefer Cloud Platform

Prefer Cloud Platform(PCP)はEverforth社から商用のクラウド・プラットフォームとしてリリースされています。

PCPはAPIベースのクラウド・プラットフォームとして各種機能を提供するとともに、運用環境なども提供しており、多くのプロダクトで活用して頂いています。

PCPは開発当初からモデル駆動開発を実現する上で、クラウドプラットフォームの存在が重要となる、という認識のもと開発を進めてきており、モデル駆動開発とのシームレスな連携を行うための仕掛けを内包しています。

SimpleModeingは、対象となるプラットフォームに依存しないニュートラルな技術体系を指向していますが、高機能のサービスを提供しているプラットフォームの能力を引き出すことも重要機能としています。

両方向からのアプローチにより、SimpleModelingではPCPの提供する高度な各種機能を活用したアプリケーション開発も可能になる予定です。

SimpleModeingのオープンソースプロダクトのみのモデル駆動開発でも十分に有効ですが、PCPを併用すると運用やセキュリティも含めたより高度なクラウド・アプリケーションの開発・運用が可能になるという形を目指しています。

メタモデル

SimpleModelingでは以下の書籍で解説したモデルをベースとしています。

これらのモデルはボクがwakhokでモデリングを教えていた時に教科書としても使用できるように執筆したものです。教育用に使えると同時に、モデル駆動開発のメタモデルとしての利用も目的の一つにしています。

ただし、このメタモデルを設計した2008年当時とは状況が大きく変わっています。

クラウド・プラットフォームがシステム開発の日用品として普及したことで、クラウド・アプリケーションを簡単に構築できるようになりました。この効果を考える補助線として、大規模エンタープライズシステムのミドルウェア群が超低価格(数億円→数千円)で利用できるようになったと考えるとよいと思います。

これらのミドルウェアを活用すると、高度なアプリケーションが簡単に構築できるわけですが、逆にミドルウェアを使いこなすスキルがいと宝の持ち腐れになってしまいます。

またモデリング段階でもミドルウェアの活用を前提としたモデリングが必要になります。

このような状況に対応するために、SimpleModelのメタモデルを再設計することにしました。

最新のアプリケーション開発事情を踏まえた検討を行うこととして、まずそのスコープについて考えました。

この後、一連の以下の記事で検討を進めています。

一通り切り口の整理ができたので、SimpleModelerに取り込む準備をしているところです。

まとめ

SmartDox, SimpleModeler, Kaleidox, Arcadiaとモデル駆動開発を構成する各種ツールの開発を粛々と進めてきましたが、それなりに動くところまで来ることができました。

当面は、従前通りツールの開発状況の紹介や、メタモデルの検討を続ける予定ですが、今年の後半ぐらいに具体的な応用につながる内容の記事を書くことできればと考えています。

2020年12月31日木曜日

SimpleModel/テクノロジー

クラウド・アプリケーションを構成する以下の各種要素技術に対して、SM2020のメタモデル設計の方針検討を行っています。

  • 関数
  • CQRS
  • Event Sourcing
  • Eventually Consistency
  • マイクロサービス
  • BigData
  • AI
  • IoT
  • DevOps

前回はCQRS、Event Sourcing、Eventually Consistency、マイクロサービスについて検討しました。

今回は、BigData、AI、IoT、DevOpsについて検討します。また、検討項目にDXを追加することにしました。

BigData

アプリケーション実行中に得られるデータを分析して、有益なデータを抽出し、アプリケーションにフィードバックして活用することが普通になってきました。アプリケーションの基本機能として、実行結果データの分析が必要となってきているといえます。

BigDataを実現する仕組みとしては、以下のような構成を取ることになるでしょう。

  • アプリケーションの実行結果の記録機構
  • アプリケーション実行結果データの収集・保管機構
  • 保管データ整理機構
  • 整理データの分析機構
  • 分析データのアプリケーション取込み機構

アプリケーションの実行結果の記録機構はログまたはデータベースに記録することになります。

アプリケーションでは採取するログデータと記録用のデータベースのエンティティが設計のポイントです。

アプリケーション実行結果データ収集・保管機構はdata lakeやdata warehouseといったサブシステムを中心としたメカニズムになります。

アプリケーションではdata lakeやdata warehouseの基盤の上でデータフォーマットの調整などが主な仕事となるでしょう。

保管データ整理機構はdata lakeやdata warehouse上に格納した生データから、分析に適した正規化を施したデータを作成します。

アプリケーションでは分析に適した正規化データの設計が必要になります。

整理データの分析機構はHadoopやSparkといった大規模演算基盤上に構築したバッチやイベントハンドラとして実現することになるでしょう。

アプリケーションでは、アプリケーションのユースケースに沿った活用方法で直接使用できるデータに変換する処理を行うことになります。

分析データのアプリケーション取込み機構は整理データから作成したデータをアプリケーション側に取り込む処理です。

アプリケーションではデータを取り込むための受け皿としてデータベースのエンティティなどを用意する必要があります。

モデリングの検討

アプリケーションの実行結果の記録機構に対して、アプリケーションではログデータのフォーマットと記録用のデータベースエンティティの設計が必要になります。

アプリケーションのモデリングの観点ではログのフォーマットはデータの最終的な利用方法から逆算して必要十分な情報が取得できるように設計することになります。このため、ユースケースモデルからの一連の連鎖でログのフォーマットまでモデルの連鎖を追跡できると理想的です。

データベースのエンティティという観点では、SM2008からeventエンティティを用意しています。eventエンティティによってアプリケーション上重要な出来事はデータベース上に記録されるので、ここからBigDataに必要な情報を収集することができます。

アプリケーション実行結果データの収集・保管機構から保管データ整理機構にかけてはデータ変換処理になります。

整理データの分析機構に対してもデータ変換処理という観点で考えればよいでしょう。

分析データのアプリケーション取込み機構に対しては、SM2008からsummaryエンティティを用意しています。整理データの分析機構で生成したデータをsummaryエンティティに書き込むことで、アプリケーションで利用することができます。

AI

AIは大きく分けて、従来型の知識ベースや推論エンジンによるAI(推論型AI)と、最近主流となっている学習型AIの2種類があります。

推論型AIに対してアプリケーションでの利用の観点では、外部のサブシステムとして用意したAIエンジンを手続き呼び出しで使用するという形になるでしょう。

一方、学習型AIの場合はアプリケーションの実行結果を使用して学習を行う必要がある点が異なります。このため、アプリケーションとは密結合のアプローチとなります。

学習型AIでは実行結果に対して何らかの計算を行い、計算結果をフィードバックするという処理になりますが、これは前述のBigDataと基本的には同じものです。BigDataの場合はSQL的な集計処理になりますが、学習型AIの場合は深層学習などの複雑な計算になる点が異なります。

モデリングの検討

SM2008ではAIシステムの設計はスコープ外で、外部のAIシステムを利用するというアプローチを取っています。SM2020でも基本的なプローチは同じです。

ただし、推論型AIと学習型AIではモデリングのアプローチもかなり異なりそうです。それぞれについて検討します。

推論型AI

推論型AIの実現方式は外部のAIシステムをRPCなどで利用するという形になります。アプリケーションのモデリングの観点ではこれをアプリケーション内のモデルでどのように表現するのかという選択になります。

シンプルなのはAIサブシステムをサービスとしてモデル化しオペレーション呼び出しで利用する方法です。

ドメイン知識などを保持したエンティティとして実現したい場合は、SM2008から用意しているruleエンティティを使用します。

学習型AI

学習型AIの場合、エンジンそのものは推論型AIと同様に外部システムとして利用する形を想定していますが、アプリケーションの実行結果のデータを使って学習を行う点が大きな相違点です。

学習データの取り込み方式は基本的に前述のBigDataと同じものになります。このためBigDataで議論したアプローチを取ることになります。

学習型AIをアプリケーションから利用する方法ですが、以下の3つが考えられます。

  • サービスへのオペレーション呼び出し
  • ruleエンティティ
  • summaryエンティティ

サービスへのオペレーション呼び出しとruleエンティティは推論型AIと同じアプローチです。

summaryエンティティはBigDataと同じアプローチです。

IoT

ビジネス・アプリケーションあってもIoTを扱うケースが増えてくるのは確実です。アプリケーションでIoTを使う場合、IoTの基盤機能そのものを自ら実現するのではなく、外部のIoT基盤を外部サービスとして使用する方式を想定することにします。

IoT基盤とアプリケーションの接続方式として以下のものが有力です。

  • RPCによるオペレーション呼び出し
  • RPCによるコールバック
  • サービス・バスによるイベント駆動
  • リアクティブ・ストリームによるイベント駆動

RPCによるオペレーション呼び出しはIoT基盤の提供するAPIのオペレーションを、アプリケーションから直接呼び出す方式です。

こちらは、アプリケーションからIoT基盤側を呼び出す方式ですが、IoTの応用では逆にいイベント駆動であるIoT基盤からアプリケーションを呼び出す方式が主となります。

RPCによるコールバックはIoT基盤にコールバックを登録し、コールバックでイベント駆動処理を行う方式です。

サービス・バスによるイベント駆動は汎用のサービス・バスにコールバックを登録し、コールバックでイベント駆動処理を行う方式です。IoT基盤で発生したIoTイベントをより汎用的な形に変換してサービス・バスに流し、サービス・バスに登録されているアプリケーションのイベント・ハンドラを起動します。より汎用的なサービス・バスのイベントを使用する点が異なります。

RPCによるコールバックとサービス・バスによるイベント駆動はいずれもイベントハンドラによるコールバックによる実現方式です。このため、アプリケーションの規模が大きくなるとコールバック地獄と呼ばれるアンチパターンに陥る可能性が高まります。

この問題の解決方法として有力なのがリアクティブ・ストリームです。

関数の回でも取り上げましたが、イベント駆動型のアプリケーションはリアクティブ・ストリームで記述するのが関数型時代の定石といえます。

モデリングの検討

IoTアプリケーションのモデリングはモデル駆動アプリケーションのモデリングという側面が大きいといえます。

イベント駆動というアプリケーションの特性からはコールバック処理がアプリケーションの軸となりますが、いわゆるコールバック地獄とよばれるアンチパターンになりがちです。

コールバック地獄を起こさずイベント駆動のアプリケーション・ロジックを記述する手法として期待しているのがリアクティブ・ストリームです。

リアクティブ・ストリームは一種のパイプラインなので、パイプラインとして抽象化することでモデル化が可能と思われます。ただ、伝統的に使用されているデータフローより動的な側面が大きいと思うので別のメタモデル要素を導入するのが妥当と考えています。SM2020ではこのあたりの検討を行う予定です。

IoTアプリケーションの特性からはイベントの階層化の設計が重要です。SM2020では以下の階層で対応するアプローチを検討中です。

  • IoTイベント
  • 生イベント
  • ビジネス・イベント

    イベント駆動で使用するイベントはeventエンティティと連携するのがSM2008のアプローチでした。SM2020でも引き続きこのアプローチを軸に考えていますが、IoT対応で発生するイベント階層との対応関係について整理していく予定です。

    DevOps

    DevOpsはソフトウエア開発と開発したソフトウェアの配備や運用などを統合した考え方で、運用配備を含めたソフトウェア開発の自動化と見える化を目指しています。

    2000年代初頭の段階では、なかなかそこまでは実現のスコープに入ってきていませんでした。

    UMLでも配備モデルを記述することは可能でしたが、当時はChefなどのDevOpsのライフサイクル全体をカバーした配備・運用ツールなどは存在せず、配備モデルも青写真レベルに留まっていることが一般的でした。

    現在ではツール類を始めとした環境が整っているので、本格的なモデル駆動開発への組み込みも可能と思われます。

    モデリングの検討

    アプリケーション・モデリングの観点では以下の点が重要と考えています。

    • 配備モデルの定義と適用
    • 運用性を高めるためのモデル要素
    • テスト性を高めるためのモデル要素

    モデル駆動開発にDevOpsを適用することを考えると、作成したアプリケーション・モデルを直接、配備や運用の記述データとして使用できると理想的です。この中心となるのが配備モデルです。配備を考えていく上では、モデルの物理的な側面を記述するコンポーネントやモジュールといったモデル要素が重要になってきます。

    SM2008では配備モデルはスコープ外でしたが、SM2020ではDevOpsの観点で配備モデルの設計を行っていきたいと考えています。

    運用性を高めるためのモデル要素としては、エンティティを監視するための属性の追加といったものが考えられます。JMX(Java Management Extension)といったプラットフォームのモニタリングツールと連動させることによって運用性が高まります。

    このような監視のための属性を抽出するためには、運用フェーズのユースケース分析なども必要になってきます。

    テスト性を高めるためのモデル要素としては、エンティティの内部状態を見える化して検証可能にするための属性追加といったものが考えられます。

    運用性やテスト性を高めるためのモデリング上のアプローチは他にも色々あると思うので、SM2020の中に取り込んできたいと考えています。

    DX

    DX(Digital Transformation)は色々な切り口があると思いますが、ここでは企業システムを最適化したビジネス・モデル、業務プロセスをサイバー空間上で新規構築すること、とします。

    基幹システムの入出力の部分を紙ベースからIT化するというものであれば、OOAD(Object-Oriented Analysis and Design)の出番は限定的ですが、ITベースのビジネス・モデル、業務プロセスを一から設計するとなると、超上流からOOADによるモデリングが必要になってきます。

    ビジネス・モデリングを受けてのアプリケーション開発では、通常のOOADによるモデリングになります。

    IoTと同様にDXの場合も、DX基盤といったものを軸にアプリケーション構築を行うようになるケースが多いと考えられるので、モデリング段階では抽象度の高いモデルで設計しDX基盤向けのコードを自動生成するというようなアプローチが有効と思われます。

    モデリングの検討

    SM2020はビジネス・モデリングはスコープ外ですが、ビジネス・モデリングで作成したモデルをモデル駆動開発用に連携させていくことでモデル駆動開発に組み込むアプローチを取っています。ステレオタイプといった拡張機構を使用して超上流のモデルをSM2020内に埋め込んだりリンクを張ったりといった連携を想定しています。

    DXアプリケーションのモデリングについては、IoT基盤との連携を軸に検討していく予定です。鍵となるモデルはワークフローと状態機械と考えています。

    まとめ

    今回は、BigData、AI、IoT、DevOps、DXについて検討しました。

    前々回、前回と合わせて以下の項目について検討しました。

    • 関数
    • CQRS
    • Event Sourcing
    • Eventually Consistency
    • マイクロサービス
    • BigData
    • AI
    • IoT
    • DevOps
    • DX

    全体としてイベント駆動アプリケーションのモデリングがさらに重要になってきていると感じました。

    このため状態機械の価値がさらに高まりました。状態機械は従来は現場の開発ではあまり活用されてきていないと思いますが、モデル駆動開発の助けを借りて本格的に活用していく必然性が増してきたかもしれません。

    また、アプリケーション全体の振る舞いを統合する概念が重要になってくると思います。一つのアプローチはユースケースをハブにして帰納的に束ねていくことでしょう。ユースケースの価値もさらに高まってきたと思います。

    特に重要と感じたのが以下の3つのフロー系のモデルです。

    • データフロー
    • ワークフロー
    • リアクティブストリーム

    SM2020ではこれらのモデルについてモデルのセマンティクスと記述方法、使い分けの指針などの整備に力をいれていきたいと思います。

    2020年11月30日月曜日

    SimpleModel/アプリケーション・アーキテクチャ

    クラウド・アプリケーションを構成する以下の各種要素技術に対して、SM2020のメタモデル設計の方針検討を行っています。

    • 関数
    • CQRS
    • Event Sourcing
    • Eventually Consistency
    • マイクロサービス
    • BigData
    • AI
    • IoT
    • DevOps

    前回は関数について検討しました。

    今回は、CQRS、Event Sourcing、Eventually Consistency、マイクロサービスについて検討します。

    CQRS

    CQRS(Command Query Responsibility Segregation)は、ざっくりいうとコマンド投入とその結果引き起こされるデータ更新の確認処理を分離するアーキテクチャです。

    従来型のCRUD(CreateReadUpdate/Delete)がデータの作成更新が同期型であるのに対して、データの作成更新が非同期で行われる点が重要です。

    CQRSのアーキテクチャを取ることによって、データ作成更新のボトルネックを分散させることができ、スケーラビリティを高めることができるメリットがあります。

    ただし、利用者からのデータの見え方が参照系と更新系で非対称になるためUX上の見せ方が重要になってきます。

    モデリングの検討

    CQRSのアーキテクチャをモデリングで扱う場合の論点として以下のものを考えています。

    • ユースケース
    • イベント駆動処理
    • サービス設計
    ユースケース

    CQRSでは、UX上の見せ方が重要となるため、適切なUXをできるだけ上流で設計していくことが重要です。

    この目的で使用することになるモデルはユースケースです。

    イベント駆動処理

    CQRSアーキテクチャに則ったアプリケーションを作成する場合、ESB(Enterprise Service Bus)を使用したイベント駆動アーキテクチャが有力な選択肢です。

    ESBアプリケーションを通常のオブジェクト指向で都度モデリングしてもよいのですが、かなり複雑なモデルになってしまいます。一方、CQRS向けのESBアプリケーションはかなりの定型化ができそうなので、うまい抽象モデルを見つけることができれば、シンプルなモデルとこのモデルからの自動生成を組み合わせて効率的な開発ができそうです。

    SM2020では、このような目的の抽象モデルの開発を行っていきたいと考えています。

    サービス設計

    CQRSでは、参照系、更新系でそれぞれオペレーションが定義されることになりますが、それぞれのオペレーションがCQRSのなかでどのような役割を担っているのか、それぞれのオペレーション間の関係がどうなっているのかという点のモデリングも必要となってきます。

    たとえば、CQRSのイベント駆動処理部の振る舞いを変えたときに、どのオペレーションにどのような影響がでるのかといった点をモデル上で検査できるというような用途を想定しています。

    Event Sourcing

    Event Sourcingは、オブジェクトの状態をイベント列を使って表現、管理していく方式です。スケーラビリティやアベイラビリティを担保する上で非常に有力なアプローチです。

    イベント列から状態を計算する方式などを実際の製品開発で試してみましたが、実装が複雑になる、性能的な要件が厳しいなどの問題もあり、なかなか一筋縄ではいかない感じです。

    このため、状態に関しては従来どおりリソース側の属性で管理するのが現実解ではないかと思います。

    ただし、イベントの発行・記録とリソースの状態遷移を連動させるという方式設計はアプリケーションの見通しをよくし、拡張性や保守性を高めるのに寄与するので積極的に採用したいところです。

    モデリングの検討

    実現方式は実行プラットフォームに依存する部分が多いと考えられます。ただし、どの実行プラットフォームでも有効な抽象モデルがあると考えられるので、これを探っていきたいと考えています。

    SimpleModelでは、従来よりイベントエンティティを重要なモデル要素として扱ってきました。

    イベントエンティティを発展させる形でEvent Sourcingとの接点を探っていきたいと考えています。

    マイクロサービス

    クラウド・アプリケーションでスケーラビリティやアベイラビリティを高めるための実現方式としてマイクロサービスが注目されています。

    マイクロサービスといっても単なるSOA的なものから、メッセージ中心の分散プラットフォームまで色々な形態が考えられます。

    一つの典型的な方式は、複数のサービス間をスケールアウト可能なRPCを使って接続するというものです。RPC層でスケーラビリティやアベイラビリティの透過性を担保できる場合は、モデリング的には通常のRPCとして扱うことで十分と思われます。

    モデリングの検討

    前述したとおり、RPC層でスケーラビリティやアベイラビリティの透過性を担保できる場合は、モデリング的には通常のRPCと同様の枠組みで扱うことになると思います。

    具体的にはサービスとオペレーションとしてモデリングします。ただし、ステレオタイプを使って、マイクロサービス用のRPCであることを定義していく形を想定しています。

    また、マイクロサービスはRPCなどを使った通信が発生するので、オブジェクト指向だけでなくコンポーネント指向の観点から疎結合・凝集、配備の問題をモデルの上で扱っていく必要があります。

    Eventually Consistency

    Eventually Consistencyは、オペレーション完了時にオペレーションによって変化したデータ更新が保証されず、オペレーション完了後のどこかの時点でデータ更新が最終的には行われることを指します。

    従来型のCRUDのオペレーションではオペレーション完了時に、オペレーション内で作成・更新したデータベースのレコードがデータベース内に完全に反映され永続化されることが保証されます。

    一方、Eventually Consistencyの性質を持つオペレーションの場合は、オペレーション完了時にこれを保証しません。つまり、オペレーション完了直後に更新したデータを参照した場合に、古いデータを取得してしまう、ということを許容します。

    アプリケーション開発的には扱いづらい性質であり、できれば避けたいところですがクラウド・アプリケーションでは以下のような理由により必要なケースが多々発生します。

    • スケーラビリティ
    • イベント駆動処理
    • バッチ処理
    スケーラビリティ

    クラウド・アプリケーションでは、多数のクライアントを同時接続できることが重要です。

    ここでボトルネックになるのはデータベースの更新処理です。

    同一レコードに対する書き込み処理が重なると、排他制御によってレスポンス性能、スループット性能のどちらも大幅に低減します。排他制御そのものは避けることはできないので、その他の部分で何らかの対策を取る必要があります。

    その有力な対策の一つが Eventually Consistency です。

    Eventually Consistency の性質を取ることによって、処理本体とオペレーション呼び出しの完了を分離することができます。オペレーション呼び出しは処理本体をバックグラウンドでの実行スケジュールするだけで完了できるので、レスポンス性能は大幅に更新します。

    システム全体のスループット性能も、バックグランド処理をキューイングするなどして平準化したり、複数の処理を一つにまとめたりすることで向上させることが可能になります。

    イベント駆動

    アプリケーション・ロジックがESBを使用したイベント駆動で実現されている場合には、処理の実行が非同期となるためオペレーションの完了に同期させることが困難です。

    この場合はEventually Consistencyを前提にオペレーションの性質を定義する必要があります。

    バッチ処理

    Webのレスポンスタイムの上限は3秒程度とするケースも多いと思いますが、アクションの実行時間がこれを超える場合にはアクションをバックグラウンドで実行することになります。この場合、当然ながらEventually Consistencyを前提としたオペレーションになります。

    モデリングの検討

    スケーラビリティ、イベント駆動、バッチ処理の観点からEventually Consistencyについて見てきました。

    共通しているのは、メインの処理はバックグラウンドで行われ、オペレーションの返却値では処理の結果を返すことができないということです。

    また、処理はバックグラウンド処理で行われますが、必ずしも成功するとは限りません。

    上記の点からアプリケーションの設計においては以下の2つの機能が必要になります。

    • バックグラウンド処理の結果確認
    • バックグラウンド処理リカバリ

    いずれも、通常のアプリケーション処理として都度モデリングを行ってもよいのですが、かなりの定型化が可能という予感もあります。

    実行時のクラウドプラットフォーム側のフレームワーク次第という面も大きいので、フレームワークとの連携を想定した上でモデリングの定型化を図っていきたいと考えています。

    ユースケース

    ユースケース・モデルの段階で以下のようなEventually Consistencyの影響を取り込んでいく必要があります。

    • リカバリ方式
    • オペレーションの特性情報

    SM2020のユースケースモデル設計で上記の性質の定義方法を検討する予定です。

    オペレーションの特性情報

    サービスのオペレーションによる更新処理に対してステレオタイプなどを用いてEventually Consistencyであることの明示することが有効と思われます。

    SM2020のユースケースモデル設計で上記の性質の定義方法を検討する予定です。

    モデリングの検討

    CQRS、Event Sourcing、Eventually Consistency、マイクロサービスといったクラウド・アプリケーションのアプリケーション・アーキテクチャの整理とモデリングでの対応について検討しました。

    検討の結果、以下の項目がSM2020で検討していく候補として抽出できました。

    ユースケース

    以下の場合にユースケース段階で考慮が必要であることが分かりました。

    CQRS
    利用者のUX
    Eventually Consistency
    データが反映されるタイミング

    上記の項目をユースケースモデルに取り込んでいく予定です。

    イベント・エンティティ

    CQRSでのESB利用やEvent Sourcingでイベントが重要なモデル要素になっています。

    SimpleModelでは元々イベント・エンティティを軸に静的モデルと動的モデルを連携させるモデル体系になっています。

    このイベント・エンティティを拡張してESBやEvent sourcingでの利用をアシストする方法について検討したいと思います。

    ESB

    CQRSやEventually Consistencyなどの実現方式としてESBが有力であることが分かりました。

    ESBアプリケーションを都度スクラッチでモデリングしてもよいですが、可能であればCQRSやEventually Consistencyの観点からのESBの使い方にフォーカスした抽象モデルを定義し、この抽象モデルを使用してモデリングと実装を連携させていくというアプローチを考えていきたいと思います。

    オペレーション

    オペレーションの特性情報として以下の項目が候補になります。

    • CQRS上の特性
    • Eventually Consistency上の特性
    • マイクロサービス上の特性
    コンポーネント指向

    ESBやマイクロサービスを用いたアプリケーションでは、疎結合/凝集、配備の問題が重要になってくるので、オブジェクト指向に加えてコンポーネント指向でのモデリングも必要になってきます。

    SM2020ではESB、マイクロサービスを視野に入れたコンポーネント指向のモデリングの枠組みを整備したいと考えています。

    まとめ

    今回は9つ挙げた要素技術の中でCQRS、Event Sourcing、Eventually Consistency、マイクロサービスを取り上げました。

    次回はBigData, AI, IoT, DevOpsについて検討を行う予定です。