OutSystems が誘う Null がない世界の罠

本記事は [OutSystems Advent Calendar 2021](https://qiita.com/advent-calendar/2021/outsystems) の23日目の記事です。
この記事の LT 版を [あつまれ OutSystems Developer! 開発者超会議 Winter2021](https://usergroups.outsystems.com/events/details/outsystems-inc-nagoya-presents-atsumareoutsystems-developerkai-fa-zhe-chao-hui-yi-get-together-outsystems-developer-winter2021/) にて行いました。その際のスライドは [Speaker Deck](https://speakerdeck.com/kata_junn/outsystems-outsystems-gayou-u-null-ganaishi-jie-falsemin) にありますので、併せてどうぞ！

# 本記事の目論見
日頃、ローコード開発プラットフォーム：OutSystems という製品関係の仕事をしているエンジニアです。
ポジティブな内容は他の方の AdventCalendar にお任せし、少しネガティブな内容をお届けしたいと思います。本記事は以下を達成したい気持ちで書かれています。

* OutSystems で開発をしている方、OutSystems を触り始めた方、ローコード製品の選定をしている方に有益な情報をお届けすること
* 中長期的には製品のより良いアップデートに繋がること
* 短期的には [この Idea](https://www.outsystems.com/ideas/213/null-values/) に LIKE を頂けること

# OutSystems は Null を意図的に排している
基本的に OutSystems で開発を行っていると Null 値を取り扱うことができません。変数の初期値は全て対応する型のデフォルト値です。
また、"Null" という名称は開発ツール（ServiceStudio）の中で数か所使用されていますが、これも同様にデフォルト値を意味します。
すなわち、
NullIdentifier() は 0 を、
NullTextIdentifier() は 空文字（""） を、
NullDate() は 1900-01-01 を、
NullBinary() は 要素無しの Byte 配列 を示します。
参考：[OutSystems で使用できるデータ型](https://success.outsystems.com/ja-jp/Documentation/11/Reference/OutSystems_Language/Data/Data_Types/Available_Data_Types) ← 型に応じたデフォルト値が載ってます

NullSafefy と言えば聞こえはいいかもしれませんが、Null を取り扱うことすらできない点において NullSafety とは異なります。なぜ Null を排したかは推測の域を出ませんが、以下のいずれかではないかなと考えています。

* 学習のハードルを低くすること
  * NullPointerException を無くす
  * Null チェックを無くす
* 製品の思想

:::note info
基本的には、と前置きしたように部分的に Null 値を実現することはできます。以下のような、OutSystems アプリ → DB/外部システム のケースで可能です。
・DB にデータを INSERT/UPDATE する
　-> SQL query で SQL 文として "NULL" という文字列を書く
　　参考：https://success.outsystems.com/ja-jp/Documentation/How-to_Guides/Data/How_to_insert_a_null_value_into_a_database_record
・REST API 呼び出し
　-> Consume REST API の OnBeforeRequest で JSON を文字列操作し null に置換する
　　参考：https://www.outsystems.com/forums/discussion/50467/assigning-null-to-string-variable-in-a-api-call/

ただし、DB/外部システム → OutSystems アプリで Null 値が渡されても型に応じたデフォルト値になってしまい、識別することは非常に困難です。
:::


## Null が無いと困ること
先述のように OutSystems では Null を実装しづらいため、開発中に困るケースに直面します。
ここからは具体的なユースケースを交え、どのような場合に困るか紹介します。

### ユースケース1：画面で数値を入力して DB に保存する

実装した画面の中に数値（整数）を入力する項目があり、任意の入力項目だとします。
OutSystems は Integer/LongInteger といった整数を扱うための型を備えており、入力項目に対応する変数はこういった型にするのが自然な実装です。
しかし、このように実装を行うと、

* 画面で何も入力をしなかった
* 画面で 0（Integer/LongInteger のデフォルト値）を入力した

このどちらにおいても、入力項目に紐づけられた変数には「0」が入ります。つまり、ユーザーが「意図的に 0 を入力した」のか、「入力をしなかった」のかの区別をすることができません。当然そのまま DB に値を保存すると「0」となります。

「外部 DB」については別の道があります。
Integration Studio には Entity の設定に Default Value behavior というプロパティがあり、デフォルト値の場合に Null に変換することが可能です。
参考：[Entity_Properties](https://success.outsystems.com/ja-jp/Documentation/11_x_platform/Reference/Integration_Studio/Element_Properties_Reference/Entity_Properties)
しかしこの場合はデフォルト値と Null の両立ができないため、「0 と Null には明確に別の意味がある」場合に対応が難しくなります。

### ユースケース2：画面で日付を入力して DB に保存する

ユースケース1 の亜種です。
OutSystems において Date 型のデフォルト値は 1900-01-01 であり、意味のあるデータとして扱われかねない値です。
仮に画面に「支払日」という項目があったとして、「未支払いだから空で入力しよう」とするのは自然な設計と言えると思います。
が、そのように操作すると「1900-01-01」が保存され、システム日付と比較して「支払い済み」と見なしてしまう障害を生む可能性があります。

ユースケース1の外部 DB についても同様となります。
1900-01-01 を保存したいのなら Null を保存することができませんし、Null を保存したいのであれば 1900-01-01 を保存することができません。

### ユースケース3：REST API で Null 値を送信する

OutSystems ではネットワークが到達可能な REST API を簡易な設定で呼び出すことができます。
その際にも Null を扱うことはできないため、自然に実装をすると「型に対応したデフォルト値」を送信してしまいます。
API の仕様によっては以下は明確に区別されているケースもあり、そういった場合に対応が難しくなります。

* キーを含めない
* キーに null が指定されている

参考：[OpenAPIにおけるundefinedとnullの設計](https://future-architect.github.io/articles/20211028b/) の"差分更新（PATCH）"

### ユースケース4：REST API で Null 値を受信する

OutSystems アプリに到達すると型に応じたデフォルト値に変換されるため、相手が「Null を送ってきた」か「デフォルト値を送ってきた」かを区別することができません。
※ Expose REST API の OnRequest で判断することは可能ですが、煩雑な実装が予期されます

## ユースケースの紹介を終えて
いかがでしたか？
これらのユースケースに直面しそうになければ、OutSystems が提供する Null がない世界はきっと心地が良いものでしょう。
しかしどれか1つでも当てはまる場合、その世界は突如として牙を剥くことを忘れないでください。

本記事では OutSystems における Null に対する特徴をまとめました。
対策のヒントを簡単に記載していますが、それぞれの詳細は別の記事としたいと思います。

## 個人的所感とまとめ
私は「早く Null の概念を実装して」派です。
Null を無くすことで習熟の容易さにつながっているかもしれませんが、ただ単に「初学者がよく躓くバグを見えにくくしている」だけに感じますし、メリットと比較して顕現するデメリットが大き過ぎます。
[NULL 撲滅委員会](http://mickindex.sakura.ne.jp/database/db_getout_null.html) にも OutSystems が行っている対応内容が一部記載されていますが、まさに以下の内容が的を射ているように思えます。Null という選択肢を提供せず、排除しているのはいささか乱暴です。

>「ガソリンタンクを持っていない車と、空のガソリンタンク」は、概念的に異なるものです

冒頭にも紹介した [Idea](https://www.outsystems.com/ideas/213/null-values/) がございますので、ご賛同いただける方は LIKE して頂けますと幸甚です。

# 参考
[OutSystemsでのNULL値について](https://blog.house-soft.info/archives/839)

LT に参加していた方々にご意見を収集した結果
![Screenshot[1]-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44216/cae72281-6d53-b478-ac28-de2aadb4a9f7.png)