[OutSystems]機能トグル（Feature toggles）を試してみる

[大規模システムの開発とデリバリー](https://success.outsystems.com/ja-jp/Documentation/Best_Practices/Lifecycle/Development_and_delivery_at_scale#.E6.A9.9F.E8.83.BD.E3.83.88.E3.82.B0.E3.83.AB)というドキュメントで紹介されている機能トグル（Feature toggles）の実装方法について。

# 機能トグルとは
上記の「大規模システムの開発とデリバリー」には
> 機能トグルはアプリケーションベースのリリース手法です。本番コードをデプロイすることなく機能の有効/無効を切り替え、システム動作を変更するための仕組みを備えています。

と書かれています。実現方法としては、以下の通り。

> 一般的に機能トグルは、アプリケーションロジックやUI要素を条件文でラップして実装し、保存された構成設定に基づいて機能を有効化/無効化します。

これを、OutSystemsで実現する方法について検討してみます。

# 確認環境
Personal Environment(Version 11.12.0 (Build 30002))
Service Studio (Version 11.11.9)
Reactive Web Appを作って動作確認してみます。

# 実装中の機能を含めたまま、リリース可能にする
OutSystemsのコード管理にはブランチ機能がなく、環境ごとに1つのTrunkのみがあるような構成です。
つまり、リリース済みのアプリケーションを追加開発中に、バグフィックスなどでリリースを行うとバグフィックスに開発中の機能がついていってしまう。

こういうときには、上位環境で直接修正（＝ホットフィックス）し、その後その修正を手前の環境にも反映するという方法があります。

その他に、開発中の機能を有効にするかどうかを判定するフラグを用意し、開発環境でだけオンにすることで対応することもできます。これが機能トグルですね。

## フラグはサイトプロパティで管理する
フラグは、環境ごとに1つしかないのでサイトプロパティに保存するのが良さそうです。
Boolean型でIsFeatureEnabledというサイトプロパティを作りました。うっかり本番で有効にならないようにDefault ValueはFalseに設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/6c93f2b2-db95-2852-8d62-15256ad9b488.png)

## 機能トグルを有効にしたときのみ表示するUI
Screen Template「Admin Dashboard」を作って動作確認用画面を用意します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/23f2f842-82c4-df31-22e4-fa3a5e70a106.png)

ここに、機能トグルを使って、特定環境でのみ表示されるUIを作ってみます。
配置する先は、タイトルの横辺り（上のスクリーンショットで黄色に塗った部分）。

### サイトプロパティをData Actionで取得する
サイトプロパティはサーバサイドにあるので、Data Actionを定義して画面から触れる場所に持ってきます。

画面を右クリック　→　Fetch Data from Other Sourceを選択。
以下のように実装します。サイトプロパティをData ActionのOutput Parameterに編集するだけ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/9641f16c-e9be-6a99-a08c-5566d3b8a467.png)

### Data Actionの出力変数で表示を切り替えるUI
If Widgetを配置し、そのConditionにData Actionの出力変数（サイトプロパティの値を編集）を設定。これで、サイトプロパティIsFeatureEnabledをTrueにしている環境でだけ、Trueの分岐のUIが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/e9834e71-b45b-f280-7d74-c3a87b4d254c.png)

## 開発担当者によるフラグ設定
この場合、開発中の環境でのみサイトプロパティのフラグをオンにします。開発環境なので作業は開発担当者。

開発環境のService Centerでサイトプロパティを定義したモジュールの編集画面を開きます。
Site Propertiesタブを開き、IsFeatureEnabledのリンクをクリックして値をTrueに設定してApplyボタンをクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/eebe2201-7967-3d7a-c4cf-88a57ddef80a.png)

開発以外の、QAと本番はデフォルトのFalseのままにしておきます。

# 特定ユーザーにのみ機能を提供する
上記とは別のパターンの機能トグルとして、特定のグループのユーザーにのみ新機能を提供し、問題がないことを確認したら徐々に提供するユーザーを増やしていく（カナリアリリース）場合。

例として、REST APIで提供されているサービスを別のサービスに切り替える事を考えてみます。同じライブラリをそのまま使いつつも、リクエストしたユーザーが特定ユーザーである場合、リクエスト先を別のサービスに切り替える。

## アーキテクチャ
こういうケースでは、OutSystemsが推奨するアーキテクチャパターンがあります。
[連携パターン](https://success.outsystems.com/ja-jp/Support/Enterprise_Customers/Maintenance_and_Operations/Designing_the_Architecture_of_Your_OutSystems_Applications/05_Integration_Patterns_for_Core_Services_Abstraction)です。といっても、外部サービスをConsumeするモジュールを直接アプリケーションから呼び出さず、間に抽象化するモジュールを1つかましておこうという程度の話ですが。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/9f33f58b-653b-efee-bdb3-ec1355c96a9a.png)

上記図の各項目は以下の通り。
- 旧サービス：移行前の、現状利用中のサービス
- Integration Service1：旧サービスをConsumeするモジュール
- 新サービス：移行後の、新サービス
- Integration Service2：新サービスをConsumeするモジュール（新設）
- Core Service：利用側アプリケーションから直接呼び出すモジュール。このレイヤを、移行要件が出てくる前から噛ませておくことで、今回のような要件が出てきたときも、利用側アプリケーションの実装に影響を与えずに変更を加えられます。

## Core Serviceに加える変更
Core Service内のPublic=YesのActionの先頭にIfを配置します。
IfのConditionで問題の特定ユーザーに該当するかどうかを判定し、該当する場合は新サービス側のモジュール（Integration Service2）、それ以外なら旧サービス側のモジュールをこれまで通り実行させる実装にします。

特定ユーザーの判定にはいくつか方法が考えられますね。
- Role：モジュール内に作成したRoleにはCheck<Role名> Actionで判定できます
- Group：Usersアプリケーションで定義した特定のGroupに所属するかどうかで判定する方法。(System)にあるGroup_User Entityが使える
- 管理用Entity：対象ユーザーを管理する専用のEntityを作成する方法

# 公式ドキュメント
[Feature toggle in OutSystems](https://success.outsystems.com/Documentation/How-to_Guides/Development/Feature_toggle_in_OutSystems)
2021/7/24時点では、日本語訳がないようです。