[OutSystems]Web Developer Specializationのサンプル問題について解説 2/2(#6-#10)

Web Developer Specializationは2023/3に追加された試験で、ReactiveのProfessional認定の条件の1つ。

試験情報のPDFと一緒にダウンロードできるサンプル問題を解説する。
全部で10問で、この記事では6問目-10問目が対象。

[Web Developer Specializationのサンプル問題について解説 1/2(#1-#5)](https://qiita.com/jyunji_watanabe/items/27d8cef635a798a6ed95)

# 6 Site Property
問題文が短いので直接選択肢を検討していく。
Site Propertyは、モジュールにおける設定値を管理するもの。
Service Studio上で定義し、実行中に（Publishやリリースを伴わずに）Service Centerで値を変更できる。

A. ×：↑で書いた通り、Site Propertyの値の変更にはPublishを要さない
B. 〇：Site Propertyの値を変更すると、キャッシュが無効化され、データベースからの再ロードが行われる。そのため、実行中にプログラムから変更されるような設定値をSite Propertyに持つことは推奨されていない。[サイトプロパティの更新を避ける](https://success.outsystems.com/ja-jp/documentation/11/managing_the_applications_lifecycle/manage_technical_debt/code_analysis_patterns/avoid_site_property_updates/)
C. ×：Site Propertyに保存できるデータの型はBasic TypesとIdentifierのみで、Entity型は含まれない
D. ×：ユーザーがログアウトすると消えるのはClient Variableの方でSite Propertyは消えない。また、Site Propertyはユーザーごとに持つ値ではない

# 7 データパージ
## 問題文の検討
あるEntityに「監査のために5年間保持し続ける（経過後は不要）」という要件がある。
月初に不要になったレコードを処理するための最適の選択肢はどれか？　という問題。

もう保持しておかなくてよいレコードは、容量や検索/更新速度等の観点から削除しておいた方がよいだろう。また、削除対象データはそれなりにあるだろうから、Aggregate->Entity Actionによる個別削除よりも、SQLによる一括削除の方が望ましい。

公式ドキュメントに[データパージ](https://success.outsystems.com/ja-jp/documentation/best_practices/architecture/data_purging/)があるが、今回の問題は見なくても解ける。

## 選択肢の検討
A. 〇：TimerでSQL要素を使って一括削除する。問題文の検討で見た通り、これが望ましい
B. ×：Entityに今有効か、を示す属性を追加。Timerで有効かどうかを判定し、無効なら属性を無効にセット。これでは不要なデータが残ってしまう
C. ×：Timerで不要なデータを取得し、ループ中で1件ずつ削除する。DB操作1回ごとにPlatform Server->Database Serverの通信が行われ、オーバーヘッドがあるので一括削除が可能ならそうしたほうがよい
D. ×：Archive用Entityを作成し、Timerで不要なデータをSQL要素を使って一括挿入する。不要なデータが元のEntityに残っているし、もう保持する理由のないデータをArchiveしている

# 8 Block/ScreenのAggregate
## 問題文の検討
大量データを持つListを表示する画面がある。
Listの各要素はBlockを配置する。Blockは
- Customer Entity1レコードからの情報
- CustomerThumbnail Entity1レコードからの画像

を表示する。

## 選択肢の検討
選択肢を、
1. ScreenでListに表示するためのAggregate
1. Screen上のListから各List Itemに渡すInput Parameter
1. Block上でInput Parameterをそのまま表示するか、あるいはその値を使ってAggregate実行するか

という観点で整理すると以下の通り。
|  | Screen Aggregate | BlockのInput Parameter | Block Aggregate |
|:-:|:-:|:-:|:-:|
| A  | CustomerからIdだけを取得  | Customer.Id  | Idを使って、CustomerとCustomerThumbnailをジョインして取得  |
| B  | CustomerとCustomerThumbnailをジョインして取得  | ジョインした1レコードをそのまま渡す  | 受け取ったレコードを表示  |
| C  | Customerを取得  | Customer1レコードを受け取る  | 受け取ったCustomer.IdでCustomerThumbnailを取得  |
| D  | CustomerとCustomerThumbnailをジョインして取得  | Blockで必要な情報だけを持つStructureを定義し、Screen Aggregate1レコードを詰替えて渡す  | 受け取ったレコードを表示  |

まず、Screen/BlockのAggregateは実行時、内部的にHTTP通信でリクエストされることに注意。
したがって、BlockでAggregateを呼ぶと、
- 「ScreenでNレコード取得するAggregate」が1回
- 「Blockで1レコード取得するAggregate」がN回（レコード数分）

の通信が発生する。これはN回の通信がオーバーヘッドであるから、基本的にはScreen Aggregateでジョインして取得することで1回で済ませるべき。
という観点から、選択肢AとCは×。

次にAggregateについては、OutSystemsが利用される属性をチェックして必要なものだけを取得するように最適化してくれる。
このとき、選択肢Bのように受け取ったレコードをまるごとInput Parameterで渡してしまうと、すべての属性が使われていると判断され、すべての属性を取得することになる。よって選択肢Bは最適ではないので×。

[Best Practices - Minimize the number of fields fetched from the database](https://success.outsystems.com/documentation/best_practices/performance_and_monitoring/performance_best_practices_queries/#minimize-the-number-of-fields-fetched-from-the-database)にAggregateの結果を最適化するときの注意点が記載されている。このページにはActionにAggregateの結果のListを渡すと最適化されないことだけが書かれているが、Personal Environmentで実験したところ、Blockでも同じ問題が発生した。

残ったDが正解。

# 9 Service Centerで確認できるログ
A. 〇：LogMessage Actionで出力したログはGeneral Logに表示される
B. ×：REST APIのHTTP通信ログを保持するのはログレベルを上げたとき。デフォルトではそこまで記録されない
C. ×：Consume REST APIでのエラーは、IntegrationsだけでなくErrorsにも表示される
D. ×：OutSystemsのログの仕組みは現在の週＋9週間分のテーブルを持っている（実際に何週分保持するかは設定できる）。ただし、Service Centerに表示できるのは直近2週間のみ

# 10 Actionの設計
## 問題文の検討
UIに表示するために、Entityから取得したデータを計算する処理がある。
この計算結果を他の場所でも使う要件があるとき、どのように設計するのがよいか。

こういうときには、

- 共通するロジックはActionに切り出す
- ActionのI/FにEntity型を渡すと、Aggregateの最適化が効かず不要な属性まで取得してしまう（# 8にリンクしたBest Practice参照）ので、必要な属性だけを持つStructure型で返す

のが望ましい。

## 選択肢の検討
図中で示された3つのソリューションは、
A. 処理の共通化を含まず、取得したデータをUIに表示している
B. 計算する処理を別のActionに切り出す。Actionは結果を、Entity型のListで返す
C. 計算する処理を別のActionに切り出す。Actionは結果を、必要な属性のみを持つStructure型のListに詰め替えて返す

よって、望ましいのはソリューションCであるから、選択肢Cが正解。