伝統的な3層アーキテクチャでのよくある履歴の取り扱いパターン
伝統的な3層アーキテクチャでは、アプリケーションの状態のほとんどをデータベースに保存し、最新の状態を取得するにはデータベースに問い合わせる必要があります。会員情報、商品情報、文書の内容など、アプリケーションの状態にはさまざまなものがありますが、それらがアプリケーション層のオンメモリには残らずデータベースに保存されているからです。各種データの中には最新の状態さえわかればよいものもありますが、一方で状態の変更履歴が重要となるアプリケーションもあります。
履歴が重要となる場合として、例えばeコマースサイトで、キャンセルされたはずの注文がシステムの不具合で配送されてしまったり、配送開始から2週間以上経つのに購入者宅にまだ商品が配送されていなかったりといった問題があったとします。 そんなときに、「注文完了」や「配送完了」といった顧客の注文の最新状態だけでなく、どの時点で何をきっかけに状態が更新されたのか、あるいは起こるべき更新が起きなかったのかという情報は、そういった問題の原因追跡に不可欠です。
またBtoB取引でよくある長期契約を管理するアプリケーションでは、契約期間中に追加料金の発生や契約内容の一部変更などのイベントが発生します。もし顧客から「次回の支払い額が自分たちの認識と違う」という問い合わせを受けたら、当該契約の履歴からなぜその支払い額になっているのか明確に説明できなくてはなりません。履歴に間違いがあれば、それらを訂正する処理を行います。
他にも、社内で使う文書管理アプリケーションでは「誰がどの部分の記述をいつ更新したか知りたい」「過去のある時点での文書の内容を確認したい」といった要求があるでしょう。gitなどソースコードのバージョン管理ツールに慣れた開発者なら、文書管理における履歴の重要性を想像しやすいと思います。
データの寿命が長くなるにつれ、さまざまな変更が加えられる可能性が高くなり、その履歴の把握が重要となります。データがビジネス上重要であればあるほど、往々にしてその変更履歴もまた重要で価値をもつのです。
伝統的な3層アーキテクチャでよく用いられるリレーショナル・データベースで変更履歴を管理する場合、最新状態を保存したテーブルとは別に、履歴を保存するためのテーブルを用意し、前者を更新すると同時に、トリガーを使って同一トランザクション内で後者を更新するのが代表的な手法です。
履歴のテーブルを別途作成する方法には、いくつかの選択肢があります。「履歴のテーブルに残すのは各時点でのスナップショットなのか差分なのか」「全てのテーブルに履歴のテーブルを作成するのか選択的に作成するのか」「履歴データ間でのJOINは必要か」などです。
このように考えることが多いので、できれば設計の指針がほしいところです。今回の記事で紹介するイベント・ソーシングとCQRSは、ここで紹介した履歴のテーブルのアプローチとは全く違う手法ですが、よりはっきりした設計指針があるのでアプリケーション全体で履歴をどう扱うか考えやすくなるでしょう。
因みにMySQLには、トリガーと履歴のテーブルを組み合わせる以外の管理手法として、バイナリ形式でテーブルの変更履歴を自動保存する機能があります。 しかし、履歴の解析をする際にはバイナリ形式のログファイルをダウンロードして解析用のコマンドを走らせる必要があるので、バイナリ形式のログは日常的に使う機能というよりは、緊急時の調査目的として用いるのに適しているでしょう。