2020の25日目の記事です。これはなに金融機関は業として金融商品や為替の取引を行ないますので、それに付随してお客様のお金や証券といった保有資産を管理が必要となります。お客様の資産ですので1円でもズレることがないよう、厳密な管理が求められます。無意識を問わず)不正な操作が行なわれていないことを担保するために重要です。一方でデータの履歴管理を実現するデータモデルとして、テンポラルデータモデルが存在します。テンポラルデータモデルは履歴管理が可能ではあるのですが、寡聞にしてどのような業務にどのテンポラルデータモデルを適用するべきかについて述べられた日本語記事はないように思われます。実際に私が関わったシステムでテンポラルデータモデルを採用したのですが、どの業務でどのテンポラルデータモデルを適用すれば良いのか、そしてその理由はなぜかというところが曖昧だったため、実装を進めた時にチームが混乱し手戻りが多く発生しました。記事もご参照ください。そこで本稿では、テンポラルデータモデルを適用する条件と元となる業務の解釈について、実務上で得られた経験を元に紹介していきたいと思います。私なりの解釈で簡易な例を使って説明を致しますので、間違いや不正確な表現が多々あるかと思います。広い心でご笑納頂ければ幸いです。業務をテンポラルデータモデルで表現するここでは、簡単にテンポラルデータモデルの私なりの解釈と実務上の適用範囲を紹介します。実生活で意識されることはないと思いますが、一般的な業務フローは2つの時間を使って構築されています。実時間と、ビジネスの都合上導入される概念上の時間です。後者の良い呼び方を知らないので、本稿では「仮想時間」と呼びます。(良い呼び名をご存知の方、ぜひ教えてください。概念上の時間なので、自由に操作が可能です。今日2020/12/20の日曜にあなたがショップXの商品の購入のため銀行振込を行なったとします。すると振込の画面のどこかに「振込は12/21に行なわれます」という風な注意書きが書かれるかと思います。この「12/21」というのが仮想時間の一例です。「振込の完了」とはなっていません。処理が全て完了を「実時間」で待つこととすると、膨大な数のしかも様々な国内外の口座への移動を全て追跡することになり、業務が破綻するからです。そこで、それぞれの処理の「実時間」における完了時刻は別途存在するにも関わらず、「仮想時間」における「12/21」という仮想的な完了時刻に、「実時間」ではまだかもしれないのに振込が完了したものと見做しているのです。(見做しているだけので、時々どこかの処理が失敗することがありえます。)他にも例えば、一週間前に行なった入金トランザクションが実は不正だった、1000円入金のところを100円しか入金していなかった、ことが判明したとしましょう。(怖いシチュエーションですね)不正な入金を取り消して正しい入金を行なう必要があるのですが、やり直しなので入金は一週間前に行なったことにする必要があります。「実時間」は巻き戻せませんので、「仮想時間」を一週間前に戻して、過去の状態からあらためて正しいトランザクションを適用するというようなオペレーションが必要になります。このようにビジネスの世界では、「実時間」とは別の「仮想時間」の概念をコンテキストにあわせて導入し、業務フローが構築されています。テンポラルデータモデルと実時間・仮想時間テンポラルデータモデルは、この「実時間」と「仮想時間」を表現するデータモデルです。Modelに入門中での表現との対応は以下のようになります。transact-timeと呼ぶことにします。transact-timeの有無の組み合わせで、以下の4つのモデルが定義されます。先ほども述べた通り業務はvalid-timeとtransact-timeを含んで構築されています。業務の履歴が残ることになる訳です。冒頭で述べた通り、金融ドメインではデータの履歴管理が求められると言いましたが、適切にテンポラルデータモデルを適用すれば要件を満たせることになります。Modelで、以下のような手順の発生する業務を表現してみましょう。完了状態C'に修正したまず手順1では状態Aが作成されます。横軸はtransact-time(実時間)、縦軸はvalid-time(仮想時間)です。状態のそばにある座標のようなものは状態の開始時点と有効期限を表しており、それぞれvalid-time基準での有効化日時(上段左)とその期限(上段右)、transact-time基準での有効化日時(下段左)とその期限(下段右)です。transact-timeの両方で最新状態を示しています。次に手順2で状態Aから状態Bに「変更」されます。この時のデータの操作は、以下の通りです。手順3も手順2と同様に状態Bから状態Cに「変更」されます。以上で手順3までが表現できました。実はここまでレコードの作成以外は、全てvalid-toを修正する操作でした。transact-toではなくvalid-toなのかが気になるかと思いますが、一旦先に進みます。(その方が説明しやすいので)次の手順4ですが、実は状態Bが間違えていた事が後から判明したので「修正」を行ないます。いう履歴を新規登録するという処理になります。新規登録すれば同じ効用は得られるので割愛します。これで手順4まで完了しました。C')とその操作の履歴(日付)が記録されていることが分かるかと思います。Modelで漏れなく表現できる事が分かりました。そのほかの3つのモデルは「変更」と「修正」のどちらか一方または両方の操作が不可となります。最後になぜ「変更」がvalid-timeで、「修正」がtransact-timeへの操作としているのかを、私なりのイメージを紹介して本項を終わりたいと思います。新規登録を期待されるのが一般的かなと思います。状態遷移が期待されるかと思います。そのため、「今日はどんな状態だっけ?」「去年の12/20はどんな状態だったっけ?」というように、最新の状態や過去の状態など遷移を参照することがありえます。先ほど述べたとおりtransact-timeは「実時間」に相当しますので、「実時間」は巻き戻ることがありませんので、transact-toに現在時刻を設定するというデータ操作は論理削除と等しくなります。一方でvalid-timeは「仮想時間」に相当しますので、巻き戻しや早送りなどの操作が可能で状態遷移を表現できます。よって「変更」=状態遷移の操作はvalid-timeの操作、「修正」=論理削除はtransact-timeの操作として解釈することがより自然となり、インピーダンスミスマッチを回避しやすくなるかなと思います。登録をする新しい状態がnullであるのと同じなので、特に区別して議論はしていません。テンポラルデータモデルの利用例の紹介ここまで長々と業務とテンポラルデータモデルの紹介をしてきました。最後によりイメージを掴みやすくするため、具体的に私がどのようにテンポラルデータモデルを適用しているか、例を挙げていきたいと思います。利用するモデル先ほどの項でテンポラルデータモデルには4つのモデルがあるという話をしました。Modelの3つがほとんどです。その理由ですが、冒頭で述べた通り設計の対象としている業務はデータ操作の履歴を残す事が重要です。Modelは、あまり使い道がありません。論理削除が不要で絶対に正しいデータ、例えばある株式の取引所の価格の推移のような、正解を誰かが保証してくれる状態遷移のデータに用いることになるかと思われます。Modelで保持していると、レプリカであることが明示されていて良いかもしれません。テンポラルデータモデルでのデータ保管では実際に株式の購入注文を例にして、テンポラルデータモデルでのデータ保管を見ていきましょう。Modelと同様に使われる頻度が低いモデルです。Modelの一種と言えます。Sourcingなシステムを構築せざるをえない事もあります。Modelを前提とした制約が発生します。これは「変更」や「修正」といったデータ操作をしたい場合には、「注文」というメッセージだけでなくそれぞれの操作に相当するメッセージが必要ということです。この実例として、金融業界のデファクトスタンダードであるFIXプロトコルが挙げられます。Modelは他にも実務上、顧客資産に関係がないもので履歴管理が必要がない重要度の低いデータにこのモデルを適用しています。Modeltransact-timeだけを持ったこのモデルは、論理削除が使えるため一番よく適用されています。例として購入注文AとBの2つで考えてみましょう。先ほど登場したキューに購入注文AとBのメッセージ(コマンド)が投入されました。etc、で注文Bを誤って受け付けてしまったことが判明します。注文Aは購入処理を進めたいが、注文Bは不正なデータなので「修正」(論理削除)したいという状況ですね。Modelの注文データに変換して保存しておくことが一つの方法です。データモデルを変換してtransact-timeを持たせておくと、万が一不正なメッセージやコマンドが連携された時でもtransact-timeで論理削除することで、後続の処理が不正なデータを無視して処理を進めることが可能になります。Modeltransact-timeとvalid-timeを持ったこのモデルは、先ほども述べた通り状態遷移が表現可能なモデルです。これまでと同様に購入注文を例として考えてみましょう。Modelで保存しました。一般的に注文は数量の訂正や取消といった「変更」の操作が行えるケースが多いです。!エンドユーザーの指示による注文の訂正や取消は、注文がなかったことになることを期待している訳でなく、注文が取り消されたことが確認可能なことを期待するかと思います。これは状態遷移が履歴として残ることが期待されているということなので、「修正」ではなく「変更」の操作に分類するのが自然です。訂正のリクエストは最初の例にあったキュー経由で送られてくるとして、注文Aというエンティティに紐づく注文データを、訂正を受けて初期状態である#1から#2へ遷移させる必要があります。Modelに拡張すると自然に履歴管理が可能となります。Modelでのデータ保管を見てきました。なんとなくイメージを掴んでいただけたなら幸いです。テンポラルデータモデルのデータ参照データ保管を見てきましたが、保管したデータは誰かに参照され使われることが前提です。実際に導入した際もデータの保管はスムースだったのですが、いざデータを参照して他の処理を進めようとなった時に結構混乱しました。最後に私が設計するシステムでのデータ参照の用途ごとの検索条件を紹介して終わりたいと思います。valid-toのような条件で検索したくなると思います。nowはサーバー時刻を使うかと思われますが、インフラ構成によってはデータ保管する処理とデータ参照する処理が異なる物理サーバーで稼働することがあります。するとデータ保管を行なうサーバーXの時刻が、参照を行なうサーバーYの時刻よりも進んでいる場合、正しい断面で最新状態が参照できないようなことが起こり得ます。以下の例だと、サーバーYが参照する時刻Tにおいて既に状態#2に遷移しているので、参照結果も状態#2が期待されますが、サーバーYの時刻TはサーバーXではT+αのため、時刻Tで参照すると状態#1が参照されることになり、不具合の原因となりかねません。全ての処理を同一サーバー時刻で実行すればこういった問題は起きないかもしれませんが、アプリケーションの設計において特定のインフラ構成を前提とするのは好ましくありません。そののため、最新の状態または状態遷移を参照したいというコンテキストでは、原則サーバー時刻は使わずに∞条件での検索とした方がベターかと思います。valid-toを検索条件とします。valid-fromよりも前の時間を指定したことにより取得出来ず、処理対象から漏れてしまうというような運用上のミスが発生しがちでした。「参照時刻」を正しく設定することはもちろん大事ですが自明ではなく間違いやすいので、間違うことを前提として、リコンサイルなど漏れを検知するような機構を別途用意して、ミスにすぐに気づけるようにしていました。ある時点の断面を取得valid-timeは状態の検索に用いられる一方、transact-timeは断面の取得に用いられます。祝日情報のようなマスタデータを配布する場合、単に最新の状態を参照してしまうと、データの完全性が担保されない可能性があります。そこで、ある時刻の断面をコピーする必要がありますが、valid-timeでフィルタリングしてしまうと状態遷移の情報が欠落してしまいます。そのため、テンポラルデータモデルの断面を取得する場合は、もう一つの時間であるtransact-timeの軸でフィルタリングをすることになります。Modelのデータとなります。この断面をシステムBに取り込む時にtransact-timeを追加することで、システムAからシステムBへのデータコピーが完了となります。コピー時刻とするのが自然です。Modelを採用するというのも自然かと思います。弊社でtransact-timeを付加する理由を書いておくと、テスト環境や本番環境テストで都合の良いようにデータの「修正」を行ないたいことからtransact-timeを付加するようにしています。最後に最初にテンポラルデータモデルの導入にあたってはチームメンバーとのミスコミュニケーションが発生、議論を経てようやく整理されていきました。本稿ではミスコミュニケーションの原因となっていた時間軸の複雑さを少しでも解きほぐすため、経験から得られた自分なりの解釈を紹介しました。設計の際に使っているモデル選択の基準や、実は結構難しくバグの原因になりやすいデータ参照方法についても紹介しました。テンポラルデータモデルは実際に使ってみるととても有効です。物理削除や直接更新が不要となったことで、データの復元や事後の操作履歴の検証が容易になり、運用の難易度は格段に下がったと感じています。一方で設計の難易度や開発コストが高めですし、脳死で設計してしまうと単に運用コストが上がるだけだったりします。皆様がテンポラルデータモデルを検討される際に、本稿が少しでも助けとなりましたら幸いです。最後にZennエンジニアのための情報共有コミュニティAboutZennについて運営会社お知らせ・リリースGuides使い方Publicationよくある質問LinksTwitterGitHubメディアキットLegal利用規約プライバシーポリシ。