トランザクションデータ(とマスターデータ)について思うところ

業務系のデータ処理では、大きくはトランザクションとマスターに分かれる。

マスターデータは特に、モデルや制御の方法が何かと面倒くさいので、よく議論になる。「マスターデータの管理の手法」というセミナーまで定期的に普通に開かれることも多い。他方、トランザクションデータ(以下TXデータ)は、普通に受け渡しのデータなので、フラットにダラダラ書いておけばよい、という扱いが大抵になる。そもそもER志向でモデルを設計すると、ほとんどは図面はマスターで占められ、TXデータはやたらなんか大量のフィールドを持つ大きなクラスがあるわいね、という扱いも多い。

あと、設計の観点からいうと、ERベースだとマスター設計が花形になる。まぁわかりやすいし、設計作業がしやすい。マスターは「構造」になり、TXデータは「構造」になりにくい。設計者は「構造」が好きだ。良くできた設計は確かに堅牢で、一定の変化にも追随できる。ある種の「美しさ」を求める人もいる。・・・なのでマスターデータについては、普通にモデルつくって、普通のノウハウを入れればいんじゃないかと思う。まぁ極論すると正規化の水準の話に落ち着くかと。

とりあえず、「業務系トランザクション・データ」について、その設計の勘所について、自分なりまとめておく。これがもうロスト・テクノロジー化しているので。

そもそもERのような静的なモデルでは、TXデータのようなフローの中にライフサイクルを持つデータタイプは捉えづらい。どうしてもモデル構成はマスターデータに寄るようになり、マスターデータなのに、TXデータがだらだらくっついて、TXデータをまとめたバインダーのようになってしまうケースもよくある。「マスター更新」なんて巨大なバッチがあるような場合は、よくみるとそれはマスターなのか、というものを多い。例えば顧客マスターなんてのは、経験的にマスターだった試しはない。

以下は自分の経験なので、鵜呑みにしないように。自分も覚え書き的に書いておくのであります。

1:外部要求レイアウト
そのシステムの系の中なのか、外なのかで処理は分かれる。外部であれば、コントロール不能な変更が入る可能性を常に頭に置いておかないといけない。もっとも多いのは、他の外部システムとの連携、社外接続のB2B系のTXデータから始まって大規模な外部システムのTXデータまで。ほぼすべて天下り的に決まってしまっているので、変化には強制的に追随する必要がある。この場合、どこでどう変換するのか?という問題が一番大きく、出入りの直前・直後段階で変換するケースがもっとも普通になる。

変換の手法やツールは様々だが、専用のツールを使い始めると大抵は猫も杓子もになり、超絶トリッキーな事をやり始めるので注意。とは言え、スクラッチで作るとメンテに死ぬので程度の問題。むしろ大事のは、どのデータが、内部のシステムのどのデータにあたるのか?それはなぜなのか?をちゃんと記録しておくこと。PMしか知らないとか、担当者しかしらないとか、あげくに誰も知らないとか時間がたつと起こる。大事なのツールではなく仕様。特にセマンティクス。このあたりはもう常識の範疇。

2:キー管理
各TXデータの”ユニーク”キーの管理。普通に、当然ナチュラルキーとサロゲートキーの両建てはしておいた方が良いことが多い。特にマスターデータと違ってTXデータでは処理に応じて、キーのユニークネスが変わる。特にナチュラルキーはその傾向がある。勢いキーになりそうなフィールドをのべつまくなしに持つ傾向はあるにはある。とにかく過剰に持つのではなく、すっきり持たせる事がコツになるが、美しさに拘るよりも汚くても冗長に持った方がトラブル時点では助かることも多い。

TXデータのキーは、データ・インスタンスそれ自体に対するキーだけではなくて、そのTXデータが属するグループに対するポインタを、そのまま冗長的に持ってしまっていることも多い。また、その場合キーに対する解説的なフィールドも附随的にもっているケースもある。これは有用性は低い(本当に必要な時は大抵、再ジョインする)が、可読性は確実にあがるのでメリットはあるにはある。この手のバックポインタ的なキーはTXデータでは、結果的に必要なことが多い。設計時に漏れないようにする。

キーのライフサイクルに十分注意すること。サロゲートの場合は、確実にデータ・インスタンスのライフサイクルと同期させる。ナチュラルの場合は「基本的に同期しない」と考えておくと、あとでトラブルが減る。これはキー情報の受け渡しをデータ・インスタンス間で行う必要が”必ず”あるので、事前に設計しておくことにつながる。これかなりの確度で忘れる。んで、見積もりオーバーで赤字ががががが。受け渡しをする場合は、データ自体のライフサイクルをどうするか(殺すのか、渡すのか、渡すときに加工するのか?)を検討すること。

3:冗長性
気がつくとコレとコレは同じだろ、というフィールドがいくらでも発生する。ただし、慌ててててて、いきなり正規化してはいけない。TXデータはビークルであるという側面は必ずあるので、どんな乗客が来ようと、以下の条件を満たす限り乗せてあげるという気持ちでいると幸せになれるかもしれない。

2-1:一応キャパがあるので、満員はマズイ。特に体重の重い人は専用のバスが必要だと思うわけですよ。なので、やたら一杯詰め込むTXデータは動かないし、コストも最終的にはかかるので、却下する。

2-2:必ず「どこかで降りる」ことが前提。要は、どこかで使われるというのが前提である。気づいたら使ってませんでした、というデータをTXで延々運んでいるということは、非常に非常によくある話です。

重力に愛されている人たちが、おしくらまんじゅうで乗っているバスが延々循環してますTXとか、普通にあるので、そうならないようにすること。ただし、多少の体重は多めに見てあげるというのが、おそらく結果としてうまくいく。

正規化がマスターの役割だとすれば、非正規化がTXの役割であるのは間違いないので、過剰なマスターの肥大化の対処にはTXをうまく設計することが重要。

4:順序性
TXデータのデータ依存関係には注意すること。アジャイルでは作るな。死ぬから。(WFをイテレートするのは別にOK。)優先度・依存度の高いデータを可能限り前倒しで作り、その処理順序をTXデータに適用すること。圧倒的に後処理での設計・実装が楽になる。

TXデータのライフサイクルとメンバーのCRUDを明示的に設計しておくこと。TXデータのデータ依存関係はそのまま処理の順序関係につながる。

5:時系列管理
まずTXデータの発生がdenseかsparseか、必ず意識すること。もっともやっかいなのは一定の時期はsparseだが、ある時期はdenseになるという場合。データ・インスタンスの管理は、なめているとあとで酷い目にあう。sparseな場合の論理的な圧縮手法をつくっておくこと。(存在しない場合は、最近のデータと同じと見なすとか)

特に日付管理には注意する。基本的に「ナチュラル」日付と「サロゲート」日付があるので意識すること。この場合はナチュラルというよりも、人為的な日付になる。たとえば、「一日を30時間時間換算する」ケース。この場合、時間がオーバーラップする。例えば10/1/30:00 ってのは、10/2/6:00と実体は同じ。これをむりやりサロゲートにしようとすると、ハードコードしたルールベースエンジンを作る羽目になるので、設計時に仕様をひつこく確認すること。

6:Validation
TXデータの正当性チェックについては、基本的に大きく二種類に分けられる。

・形式チェック
テーブル・レイアウトやファイル・フォーマットのチェックになる。スキーマとの整合性の確認で処理する。正当性の確認のポイントは、特に型・範囲・コード体系・存在の有無等になる。普通にあるので、普通にやる。
エラーデータは基本は即時に却下。但し、チェックはデータ・インスタンスの最後までやることが必要。

・意味チェック
フィールドの内容の整合性の確認。TXのデータ内部の整合性の確認と、TXデータとシステムの内部での整合性の確認の2種類がある。いずれも基本的に結合処理になる。内部データの整合性のチェックは、データの冗長性の話の延長の上にあることが多いので、特に「可読性」という点で確認するとよい。その他のチェックは、基本的にコンテキストによって真偽が判別する。かならずエラー処理と処理継続性について検討すること。

7:ライフサイクル
TXデータのライフサイクルは、可能な限り「実体に合わせる努力をする」と良い事が多い。この場合仮想的にTXデータを想定する必要があり、うまくモデルを作っておくと非常に品質が高くなる。すなわち、データと実体を分離させる手法がとれる。

実装上は複数のTXデータで、一つの「論理的なTXデータ」を制御するという形にする。これは業務的に、”取引”というものが、生成・更新・消滅する過程を掴み、それに応じてTXデータを制御する。単一のTXデータでは限界があるので、うまくデザインすること。典型的なオブジェクト指向でのモデリングに近いが、静的なものではなく動的なものとしてTXデータとその対象たる実体を捉える。対象となる実体の、生成・更新・消滅を意識しながら、実装TXデータとしてのライフサイクルをデザインする。とくにユニーク・キーの取り扱いがポイント

TXデータへの処理は、典型的なSTS分割法を利用してデザインすること。迷ったら、馬鹿の一つ覚えのSTS分割でなんとか行ける。どこから来て、何をして、どこへいくの?と多層的に分析して、分解していく。多層的に構成するのがコツで、最初は俯瞰しながらざっくり割っていくと良い。

8:構造
TXデータの構造は、概ね以下の3っつになることが多い。
・フラット構造
明細をバラして、それぞれにヘッダー情報を添付した形。典型的なTXデータ。問題ない。
・ツリー構造
ヘッダーを頭にして、明細をツリー上に展開する形。途中でリストになったり、フラットになったりすることが多い。
・List構造
バインダー形式になる。大抵の場合はリストはネストする。
今のところは以上の三つのタイプの操作を考慮しておけば、大抵のTXデータの処理の抽象化は可能である。
以上の操作をハンドルできるF/Wとか考えると吉。

9:エラー処理
個人的にTXの設計でもっとも重要なものはエラー処理だと思っている。
エラー処理は経験的に以下の4つ(業務系で3っつ)

システム・エラー
まず、システム・エラーは完全に業務例外と分離すること。これは利用する言語に依存する部分はある。業務例外でシステム例外と同じリターンまたは例外を投げることは、可能な限りさける。業務例外でnullとかは最低だと思え。(=システム例外と区別するハンドラーが別個必要になる)

致命的エラー
ストップエラーにあたる。以降の処理が完全に無駄になるので、TXデータとしては、失敗させてそのままレポートする。
復帰方法を必ず考える事。データ・インスタンス単位でストップになることがあるので、リトライ時に合流できるように設計すること。

警告エラー
続行エラー。ストップさせるよりも続行させるメリットの方が大きい場合にとる。結合エラーが発生しても、そもそもの結合先に問題があるようなケースも考えられる。レポートが重要で、妙なWarningに埋もれさせない手法が大事。

発見的なエラー
そのシステムの系の中では発見できないエラー。システム内部では整合性はとれているが、外部から見ると不整合になっているようなケース。{A,B,C}のセットでそれぞれ問題がないが、本来は{A,B,C,D}のセットであることが内部からは認識されてないようなケース。不在の証明とか。ちょっと違うけど、生徒の出席確認で「居ない人、手をあげて!」ってやる感じ。