(OutSystems) Service Actionチュートリアル

OutSystems 10のMaistream Supportが[2020/5/31で終了します](https://success.outsystems.com/Support/Enterprise_Customers/OutSystems_Support/End_of_Mainstream_Support_for_OutSystems_Platform_10)。
このドキュメントは、OutSystems 10環境で開発してきたチームメンバーにOutSystems 11の新機能を説明する資料として作成しました。
RESTとServer Actionの中間的（RESTの独立性とServer ActionのOutSystems環境による様々な支援）機能として、バージョン11で追加されたのが、Service Actionです。

# Service Actionの概要
以下の2つの特長を合わせ持つ、中間的な機能のActionです。
- Server Actionの特徴
    - OutSystemsによる厳密な型チェック
    - Manage DependenciesでOutSystemsの要素として直接探して参照できる
    - 変更時の影響チェックができる
    - Parameterとして直接OutSystemsのEntityやStructureが渡せる
- RESTの特徴
    - 他のモジュールから独立性の高いサービスを提供できる
    - 参照先を更新したからといって参照元の更新を強制されない

ただし、OutSystemsの外部環境に対するサービス提供はできません。Javaや.NETから呼びたければREST/SOAPで実装します。
[OutSystems 11ではContainerに対応](https://blog.house-soft.info/archives/1447)してます。Containerに配置するのに相性が良いActionです。

# チュートリアル作成環境
Personal Environment(Version 11.7.3 (Build 5968))
Service Studio(Version 11.6.27)

# サンプル
https://www.outsystems.com/forge/Component_Overview.aspx?ProjectId=7877
CalculateConsumptionTaxを参照してください。

# アプリケーション作成
Service StudioでNew Applicationをクリック。
New Applicationダイアログで、「Service」を選択して「NEXT」ボタン。こうして作成したServiceアプリケーションには、ServiceモジュールとExtensionモジュールしか作れません。
![qServiceAction1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/aa8a9000-3145-e64f-9410-17b3d94cad1d.png)

アプリケーション名を入力して、「CREATE APP」ボタンをクリック。
![qServiceAction2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/8a6e7e56-cc10-86f2-ca98-a5346586e25c.png)

ここではServiceアプリケーションを作りましたが、Serviceモジュールは、他の種類のアプリケーション(Traditional WebやReactive Webなど）に作成することも可能です。

# モジュール作成
作成したアプリケーションが開くので、Choose module typeで「Service」が選択されていることを確認して、「CREATE MODULE」ボタンをクリック。
![qServiceAction3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/68aab87b-0bcf-7f4c-7f55-b2cbce8078f6.png)

作成済みのアプリケーションにServiceモジュールを追加するときは、アプリケーションを開いたら、Modulesの下の方にあるADD MODULEをクリックすると、上と同じモジュール作成画面になります。

Serviceモジュールは、独立性の高いロジックを作成するためのものなのでUI要素（Service StudioのInterfaceタブすらない）やセッション変数は作成できません。

# 簡単なService Actionを作ってみる
Service ActionはServiceモジュールにしか作れません。作成したServiceモジュールをService Studio内で開いてください。
Service StudioのLogicタブ > Service Actionsを右クリックして、「Add Service Action」を選択して作成します。
![qServiceAction4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/601b5850-a257-fa3d-0d30-ccb5a0b3ce18.png)

以下のActionを作ってみました。引数で渡された数値に消費税額を足して税込の金額を返す簡単な処理です。

Name：CalculateConsumptionTax
Description：消費税込み価格を計算する
Input Parameter：Price(消費税抜きの金額、型はCurrency、必須）
Output Parameter：PriceIncludingTax（消費税込の金額、型はInteger）

![qServiceAction5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/08389042-81da-3ca1-7bdc-5deacdbf735b.png)

左がService Actionの実装で、右はService Actionから呼び出しているServer Actionの実装。このように、Service Actionの内部はServer Actionと同じように作成できます。ただ、処理の実態をServer Actionに切り出しておくと、OutSystems外部にも同じサービスを公開したくなったときや、他のサービスの一部に組み込むときに処理を共通化できます。

なお、Service Actionには、Publicプロパティはありません。他の要素と違い、Public=Yesにしなくても（Publishすれば）他のモジュールに公開されます。恐らく、他のモジュールに機能提供するServiceを作るためのものだからだと思います。

# 動作確認
Traditional Webでテスト用モジュールを用意して動作確認してみます。
（アプリケーション作成時に「Traditional Web」、モジュール作成時にもChoose module typeで「Traditional Web」を選択して作成する）

まずは、Manage Dependenciesダイアログ（ショートカットキーCtrl+Q）で、作成したService Actionへの参照を追加します。
（左側でService Actionを作成したモジュールを選択し、右側でService Actionの左のチェックボックスを入れ、「APPLY」ボタンをクリック）
![qServiceAction6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/85f5d949-9295-0f23-b25c-c44cb01daec4.png)

使い方は、Server Actionの場合と変わりありません。
ここでは動作確認用に、数値入力用Input、Screen Action（Service Actionを呼び出し、結果を変数に格納するだけ）、結果の変数を表示するExpressionを画面に配置しました。Service Action「CalculateConsumptionTax」のInput ParameterはInputと紐付けた変数にします。
![qServiceAction7.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/77c52043-3a75-9c1e-15c2-b34e9ed8e50a.png)

実行結果例
![qServiceAction8.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/a67fd4d2-e8c8-b07f-c31d-83f8e7aae878.png)

Service Actionの実行履歴はService Center内の、Monitoring > Service Actionsで確認できます。

# モジュールの更新の影響を確認してみる
## 強い依存関係と弱い依存関係
モジュール間、要素間の参照関係が、「強い依存関係」と「弱い依存関係」に分類されました。
強い依存関係であれば、参照先要素（例：Server Action）の実装を変更したとき、参照元モジュールで参照をリフレッシュしてPublishしないと変更が（参照元モジュールの視点からは）反映されません。

それに対して弱い依存関係であれば、I/Fを変更せず、実装のみを変更した場合、参照のリフレッシュもPublishもしなくても変更後のロジックは直ちに適用されます。Service Actionに対する参照はこの弱い依存関係に分類されます。

公式ドキュメント：[強い依存関係と弱い依存関係について理解する](https://success.outsystems.com/Documentation/ja-jp/11/Developing_an_Application/Reuse_and_Refactor/Understand_Strong_and_Weak_Dependencies)

## Service Actionの実装を変えてみる
動作確認で作成したActionの実装のみを変えて、影響を確認してみます。
Service Actionから利用しているServer Action「ConsumptionTax_Calculate」では消費税率をLocal Variableに持っているので、これを10%から8%に変更してみます。

実行結果は以下の通り。
![qServiceAction10.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/fae694ef-9d44-f208-e234-b4ff67b04c18.png)

つまり、参照元モジュールにPublishを要求せずに参照先モジュールだけの更新ができました。

## Service ActionのI/Fを変えてみる
次はI/Fを変えてみます。
出力変数の型を変更してみました（Integer　=>　Currency）。この場合も参照元の更新とPublishはせずとも変更は有効になっています。整数型から小数形式の型に変更したため、小数点以下の値が戻り値に追加され、その値が丸められて繰り上がった感じですね。
![qServiceAction11.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/b099469f-c504-83fd-4278-6c0868440be8.png)

ログに出ている戻り値を比べてみましょう。
Service ActionのI/F変更前：{"outputParameters":{"PriceIncludingTax":115}}
Service ActionのI/F変更後：{"outputParameters":{"PriceIncludingTax":115.5}}

ただし、実装は参照を更新しなくても反映されますが、参照元では以下のように更新を要求されます。
![qServiceAction12.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/27a8b106-eaef-a634-3ed3-2685f08ab965.png)

更新後に再実行すると、こうなりました。今度は、参照元のモジュールから参照しているService Actionの出力変数型が小数であることを正しく把握するため、丸めずに表示してくれました。
![qServiceAction13.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/5f2482a7-1569-e4a1-4d03-5ed1e6659186.png)

このように、弱い依存関係であっても、I/Fを変更したら参照更新とPublishが必要です（しなくても内部で動作するロジックは変更後のもの。I/Fの整合性を取るために更新が必要）。