👻

僕の感じたインピーダンスミスマッチ

2024/02/21に公開

まえがき

インピーダンスミスマッチをネット検索しても「オブジェクト指向言語とリレーショナルデータベースとのギャップ」との説明で具体的な例が記載されている記事が少ないと感じました。この記事では筆者が感じているインピーダンスミスマッチを具体例を持って記述することで、筆者の認識が世間のエンジニアとズレていないかを確認したいと執筆しました。
この記事でのインピーダンスミスマッチの例についてどのように感じたのかをコメントにて教えていただけると嬉しく思います。

概要

筆者が感じるインピーダンスミスマッチは以下である。

  1. RDB:挙動の違いを区分値で表現 ー オブジェクト:挙動の違いを多態性で表現
  2. RDB:時系列を行為の履歴として表現 ー オブジェクト:時系列を状態として表現

筆者は特に2.がよく出現する典型的なインピーダンスミスマッチと考えている。

挙動の違いの表現方法

例として料金の支払いで考えるとします。
支払は、銀行振込で行われるものとして振込手数料を自身負担か先方負担かが選択できるとします。この例における振込手数料の負担についてをここでは「挙動の違い」として記載します。
「挙動の違い」の表現方法はRDBとオブジェクト思考ではそれぞれ以下のように表現されると考えています。

  • RDB:区分値で表現
  • オブジェクト指向:多態性で表現

RDB

支払の例の場合、RDBでは筆者は以下のように表現しました。

支払No 金額 振込手数料区分
1 100 1
2 300 2

挙動の違いは「振込手数料区分」として区分値で表現されています。
RDBではデータ構造をテーブルとして表現するため、ロジックを直接記述することはできません。そのため支払というデータ構造に支払の種類の違いを表現する区分値が追加しました。
RDBでは挙動の違いは区分値として表現されています。

オブジェクト指向

支払いの例の場合、筆者はオブジェクト指向では以下のようにコーディングしました。

public interface Payment {
    public void transfer();
}

public class PaymentWithCharge implements Payment {
    @override
    public void transfer() {
        // transfer amount with charge
    }
}

public class PaymentWithoutCharge implements Payment {
    @override
    public void transfer() {
        // transfer amount without charge
    }
}

挙動の違いは「多態性」として表現されています。

RDB-オブジェクト指向の比較

RDBではデータ構造を表現するためロジックを直接記述することができず、区分値によって表現対象(=支払)を分割し、異なる性質を持つことを表現しています。
オブジェクト指向言語を含むプログラミング言語はロジックを直接記述します。オブジェクト指向言語では、ロジックが違えば異なるクラスを定義すると思います。そのためRDBで区分値が異なる(=挙動が違う)場合は別クラスとして定義し、同種のオブジェクトであることをInterfaceを通した多態性で表現されることが多いのではないでしょうか。
まとめると、挙動の違いを同一テーブルの区分値違いとして表現するRDBと、ロジックが異なるため別のオブジェクトとして表現するオブジェクト指向の差をインピーダンスミスマッチと筆者は感じています。

時系列の表現方法

ここでも例として料金の支払いで考えます。支払いでは以下の行為を行うものとします。

  1. 支払が発生する
  2. 振込する
  3. 振込の確認をする

振込の確認は振込作業1回について1回確認作業をするとします。
「時系列の表現」の表現方法はRDBとオブジェクト思考ではそれぞれ以下のように表現されると筆者は考えています。

  • RDB:行為の履歴として表現
  • オブジェクト指向:状態として表現

RDB

支払の例について、以下のようにテーブル定義しました。

支払書テーブル

支払書No 金額 振込手数料区分
1 100 1
2 300 2
3 500 1

振込テーブル

振込No 支払書No 振込日 実施者ID
1 1 2024-02-01 1
2 2 2024-02-02 1
3 3 2024-02-03 2

振込確認テーブル

振込確認No 振込No 確認日 実施者ID
1 1 2024-02-03 2
2 2 2024-02-03 2
3 3 2024-02-05 3

人によって異なるテーブル定義になると思います。(特にActive Record思想のWEB FWをよく利用する方とは異なっているのではないでしょうか)
筆者が意識している点は以下です。

  1. データの発生タイミングでテーブルを分割する。
  2. 行為を表現する場合は実施の履歴としてDB保存する。

1.は正規化をすることと深く関係していると考えています。2.はDBには状態を保持せず行為の履歴として保存するということになります。

レコードの登録のされ方は以下となります。

  1. 支払義務が発生する:支払書テーブルにINSERT
  2. 振込する:振込テーブルにINSERT
  3. 振込確認する:振込確認テーブルにINSERT

各行為を実施するとその行為を表すテーブルにレコードがINSERTされます。つまり行為の履歴としてテーブルに保存しています。

オブジェクト指向

支払の例について、オブジェクトでは以下と表現しました。

public class Payment {
    public static int STATE_NOT_TRANSFER = 1;
    public static int STATE_TRANSFERED = 2;
    public static int STATE_TRANSFERED_CHECKED = 3; 
    
    private int state;
}

メンバ変数stateとして状態として表現しています。

RDB-オブジェクト指向の比較

RDBでは振込処理がされると振込テーブルにレコードがINSERTされ、振込確認処理をすると振込確認テーブルにINSERTされるとして時系列による変遷を表現しました。つまり行為の履歴として保存しています。
オブジェクト指向では、それらの行為の履歴の結果、どのような状態になったかをオブジェクトで表現しています。(もちろんこれはアプリケーションの関心事が状態にある場合です。振込行為そのものに関心がある場合は、Paymentクラス以外とは別に振込クラスが出来、Paymentクラスのstateメンバ変数は無くなるでしょう)
まとめると、行為の履歴を保持するRDBと、状態を保持するオブジェクト指向の差異をインピーダンスミスマッチとして筆者は感じています。筆者は特にこちらの「履歴」と「状態」の差異が頻繁に出現するインピーダンスミスマッチと考えています。

あとがき

筆者はいわゆるWEB業界に来る前にSI業界でSEとして働いていおり、その頃にTMという業務分析手法兼テーブル設計手法を経験しています。そのため、筆者のテーブル定義はその考え方に大きく寄った考え方をしていると思われます。WEB業界の経験が長い人から見ると違和感があったのではないでしょうか。

筆者はSI業界からWEB業界へと転職したのですが、そこで出会ったActive Recordの思想に衝撃を受けました。「時系列の表現方法」で補足情報として記載した箇所のテーブル設計がActive Recordライクな考え方だと筆者は感じているのですがみなさんはどうでしょうか?(極論ですがActive RecordはDBを存在しないとして設計する思想と感じています。)
実はこの記事はActive Recordについて思考した記事を書きたく前日譚として記述した記事です。後日Active Recordについての記事を作成予定のため、できましたらリンクを追記しようと思っています。

作成しました。こちらです↓
https://zenn.dev/naitsu/articles/ed53b91004dce9

最後に、この記事はみなさんの思うインピーダンスミスマッチと筆者の思うインピーダンスミスマッチが一致しているのかを確認したいため執筆しました。この記事を読んで感じることがあればぜひコメントを記載していただければと思います。

Discussion