asopi tech
asopi techIndie Developer
構造化する技術(第1回)― 乱雑な情報から複数の射影へ

【2026年8月版】

構造化する技術(第1回)― 乱雑な情報から複数の射影へ

公開日: 2026/08/13
読了時間: 約 49分

同じ会社の記録でも、システムごとにIDや会社名の表記が違えば、売上は別々に集計される。人は同じ会社だと分かるのに、コンピューターは同じ顧客として扱えない。

この三部では、身近なデータをコンピューターで処理できる形へ変える方法を扱う。

第1部では、名寄せと日常のコピペ作業を例に、何を一件として扱い、用途ごとのテーブルや保存形式をどう決めるかを説明する。

1. 身近なデータ整理としての構造化

1.1 顧客名簿の名寄せで苦労する

顧客管理、請求管理、営業支援、問い合わせ管理から、それぞれCSVを出力したとする。各ファイルには、同じ法人らしいレコードが一件ずつ含まれている。

customer-management.csv
customer_id,company_name,address,phone
C-1042,株式会社アソピテック,東京都千代田区丸の内1-2-3,03-0000-1234
billing.csv
billing_account_id,bill_to_name,bill_to_address,tel
B-7780,(株)アソピテック,東京都千代田区丸の内1丁目2番3号,03 0000 1234
sales.csv
account_id,account_name,office_address,main_phone
S-223,ASOPI TECH Inc.,"1-2-3 Marunouchi, Chiyoda-ku, Tokyo",+81-3-0000-1234
support.csv
organization_id,organization_name,address,phone
T-9981,アソピテック,千代田区丸の内1-2-3,

四件のレコードは、各システムのIDを使ったままでは同じ法人のデータとして集計できない。人が読めば同じ法人らしいと推測できるが、IDの列名はcustomer_idbilling_account_idaccount_idorganization_idに分かれている。ID値もC-1042B-7780S-223T-9981で一致せず、会社名、住所、電話番号の表記も異なる。そのまま会社別に集計すれば、同じ法人の取引が四つの小計に分かれてしまう。

複数のデータから同じ顧客を特定してまとめる作業(名寄せ)では、四つのシステムで会社名や電話番号に当たるカラムを対応付ける。そのうえで、どのレコードが同じ法人を表すかを判断し、共通の顧客IDへ紐付ける。

ExcelやWordで見やすく整形された注文リストや記入フォームから、顧客名、商品名、数量、配送先を別の発注用Excelへ毎回コピペしたことはないだろうか。コピペ先の表では、注文ごとに1行にするか、商品ごとに1行にするか、顧客名や数量をどの列に入れるかを決めるのに困ったことはないだろうか。

一件の単位と列の対応を決める作業は、冒頭の名寄せにもある。法人ごとにレコードをまとめるため、会社名、住所、電話番号を見比べ、何万件ものレコードを手作業で共通の顧客IDへ紐付けていなかっただろうか。

構造化とは、何を**エンティティ(管理単位)**とし、どの属性、関係、制約を持たせ、どのデータ型とフォーマットで表すかを決め、情報を検索、比較、集計、検証、推論できるようにすることである。

1.2 表記の一致と法人の同一性

customer-management.csvcompany_nameにある株式会社アソピテックから株式会社を除き、billing.csvbill_to_nameにある(株)アソピテックから(株)を除くと、どちらもアソピテックになる。前者のphoneにある03-0000-1234からハイフンを、後者のtelにある03 0000 1234から空白を除くと、どちらも0300001234になる。

sales.csvmain_phoneにある+81-3-0000-1234では、国番号+81を国内電話番号の先頭0へ変換する必要がある。support.csvphoneが空なので、organization_nameアソピテックaddress千代田区丸の内1-2-3を使って候補を絞る。

表記の正規化同一性の判断は、名寄せでは別の処理として設計する。冒頭のデータでは、(株)株式会社へ置き換える正規化によって入力の違いを減らせるが、株式会社アソピテックASOPI TECH Inc.が同じ法人かどうかまでは判断できない。Unicodeにも、同じ文字に見える複数の符号列を比較可能な形へ変換する正規化形式が定義されているが、これは法人の同一性を決める規則ではない。

同一性の判断に使う属性は、照合する母集団と用途によって変わる。冒頭のデータでは、名前、住所、電話番号、法人番号、情報源、観測した時点が候補になる。NISTのデジタルIDガイドラインも、アイデンティティ解決を、母集団の中で一人または一法人を区別するために必要な最小限の証拠と属性を集める処理としている。識別子を付ける前に、何を同一とみなすかという規則が要る。

同一性の判定条件には、誤って統合する場合と同じ法人を分ける場合の両方を考慮する。電話番号の一致だけで統合した場合は、代表番号を共有する別法人までまとめる可能性がある。完全一致だけを認めれば、sales.csvの英語表記は別法人として残る。

名寄せ(レコードリンケージ、重複排除、エンティティ解決)の処理は、複数の属性を使う比較、比較する組み合わせを絞る候補生成、同じエンティティかどうかを決める同一性の判断に分かれる。米国国勢調査局のレコードリンケージ研究の概説も、氏名と住所の標準化、近似文字列比較、候補の検索を別の処理として説明している。

請求先として同じ会社をまとめる基準と、法的に同一の法人であることを確認する基準は一致しない場合がある。企業グループを一顧客として扱う営業分析では複数法人をまとめる一方、契約や請求では法人ごとに分ける。同じレコード群に対しても、用途が変われば同一性の境界が変わる。

1.3 一行は何を表すのか

構成管理台帳に次の一行があるとする。

server-deployments.csv
server_id,application_id,environment,location,owner_team
srv-01,order-api,production,Tokyo,Platform Team

この一行をサーバー配置の一件と定義すれば、srv-01order-apiproduction環境で実行し、東京に配置され、Platform Teamが管理するという関係を表せる。行数はサーバー配置の件数になる。

担当チームの責任者や連絡先まで管理する場合は、チームを独立したエンティティとして識別し、owner_teamから参照する。

顧客データや構成管理台帳とは別の例として、注文データでは粒度の問題が加わる。一つの注文に複数の商品がある場合、一行を注文として使えば複数の商品を同じ行へ入れる必要がある。一行を注文明細として使えばorder_idが複数行に現れ、行数は注文数ではなく明細数になる。決済と配送も別の件数を持つため、注文、注文明細、決済、配送のどれを一行とするかを定め、それぞれをIDで関連付ける必要がある。

データを整理する出発点は、利用者が何を知り、どの操作を実行したいかというユースケースである。同じ法人の記録をまとめる注文数を数える失敗した決済だけを再実行するといった目的を、検索、比較、集計、検証、推論、実行の処理へ分解する。エンティティの境界、粒度、同一性、関係を定め、現実の記録で観測した属性と値を情報源や観測時点とともに収集する。用途別の表現では必要な属性を選び、フィールド、データ型、フォーマットを決める。保存方式は、同じユースケースが要求する更新頻度、ランダムアクセス、一括処理、保持期間から選ぶ。

1.4 検索、集計、関係探索で変わる表現

第1.1節の四件を名寄せし、同じ法人へC-001を割り当てたとする。ここからは、名寄せ後の顧客、契約、法人間の関係を記録した三つの架空データを例に使う。

customer_master.csvは、顧客IDに法人名、英語名、住所を対応付けた顧客マスターである。

customer_master.csv
customer_id,legal_name,english_name,address
C-001,株式会社アソピテック,ASOPI TECH Inc.,東京都千代田区丸の内1-2-3
C-002,株式会社アソピテック西日本,ASOPI TECH West Inc.,大阪府大阪市北区梅田4-5-6

contracts.csvは、契約ごとに顧客ID、締結日、金額、通貨を記録した契約明細である。

contracts.csv
contract_id,customer_id,signed_at,amount,currency
K-1001,C-001,2026-07-01,1200000,JPY
K-1002,C-001,2026-07-18,800000,JPY
K-1003,C-002,2026-07-20,500000,JPY

company_relations.csvは、C-002C-001の子会社であるという法人間の関係を記録している。

company_relations.csv
from_customer_id,to_customer_id,relation
C-002,C-001,subsidiary_of

処理によって、使用するファイル、属性、演算は異なる。第1.4節のデータでは、customer_id = C-001で一社を取得する処理は、customer_master.csvへIDから到達できればよい。アソピテックを含む会社を探す処理には日本語名と英語名を検索語へ変換したインデックスが要る。C-001の7月の契約総額を求める処理ではcontracts.csvの二件を合計し、結果は2000000 JPYになる。企業グループ全体を調べる処理では、company_relations.csvをたどってC-002も集計範囲へ加える。

同じ顧客データを使っていても、ID検索、名称検索、月次集計、関係探索では必要な項目と演算が異なる。用途に必要な性質を選び、目的に合う表現へ写す操作を射影と呼ぶ。

2. 射影と正本

2.1 射影で残る情報、失われる情報

第1.4節のレコードから、用途別に次のファイルを作ったとする。

customer_name_index.csv
normalized_name,customer_id
アソピテック,C-001
asopitechinc,C-001
アソピテック西日本,C-002
asopitechwestinc,C-002
customer_monthly_sales.csv
month,customer_id,total_amount,currency
2026-07,C-001,2000000,JPY
2026-07,C-002,500000,JPY
customer_relation_graph.csv
customer_id,related_customer_id,relation
C-001,C-002,has_subsidiary
C-002,C-001,subsidiary_of

直前に示した三つの射影には、用途に必要な情報だけが残る。customer_name_index.csvには検索語と顧客IDが残るが、住所と契約額は入らない。customer_monthly_sales.csvには月別の合計が残る一方、K-1001K-1002の区別や各契約の締結日は失われる。customer_relation_graph.csvには二社の関係が残るが、名称と売上は含まれない。

射影は、用途に必要な情報を残すと同時に、それ以外を捨てる。この例で情報損失を明示すれば、月次集計から個々の契約を復元できない理由と、再計算にcontracts.csvが必要な理由を説明できる。用途に不要な情報は射影から省き、失った情報が必要な処理には正本または別の射影を使う。

2.2 正本からの再生成

第2.1節のcustomer_monthly_sales.csvに相当する結果は、PostgreSQLなら次の定義から生成できる。

customer_monthly_sales.sql
CREATE MATERIALIZED VIEW customer_monthly_sales AS
SELECT
  date_trunc('month', signed_at) AS month,
  customer_id,
  SUM(amount) AS total_amount,
  currency
FROM contracts
GROUP BY date_trunc('month', signed_at), customer_id, currency;

この例では、個々の契約を保持するcontracts正本であり、クエリの結果を保存したcustomer_monthly_sales派生データになる。K-1001amount1200000から1300000へ修正すると、マテリアライズドビューの再生成後にC-001の合計は2000000から2100000へ変わる。集計結果の2000000だけを直接修正すると、次の再生成で変更が消え、どの契約を根拠にした金額かも説明できない。

エンティティの定義、アプリケーション別のビュー、格納形式とアクセスパスを分ける考え方は、ANSI/X3/SPARCのDBMS参照モデルにもある。1985年のNBS報告書では、概念スキーマが対象組織のエンティティ、属性、関係、制約を記述し、外部スキーマが特定のアプリケーションから参照するフィールドとレコードをビューとして提示する。内部スキーマはデータの格納形式とアクセスパスを扱う。

正本は、顧客、契約、企業関係などのデータ種別ごとに分割できる。この例では三つのファイルをcustomer_idで関連付けている。各フィールドの正本と派生データを生成するクエリが分かれば、派生データを再生成できる。

正本と派生データの間では、入力データ、出力するフィールドと集計値、失われる明細、不変条件、鮮度、再生成方法のうち、ユースケースに必要な項目を決める。PostgreSQLのマテリアライズドビューは、定義するクエリを保持しつつ結果を保存し、REFRESH MATERIALIZED VIEWで内容を置き換える派生データの一例である。

3. 三つの分類軸と横断する文脈

顧客データをRDBへ保存し、名称検索用のインデックスを別に作る場合は、テーブル定義と検索方法で決める内容が異なる。テーブル定義では、顧客テーブルのcustomer_idを主キーにし、legal_nameaddressをカラムとして持たせ、契約テーブルのcustomer_idを外部キーにする構成を選べる。検索方法では、customer_idの完全一致検索にB-treeインデックスを使い、会社名検索用にlegal_nameを検索語へ分け、検索語からcustomer_idへたどる逆引きインデクスを作るといった選択がある。

DBの外でも、何を一件として扱い、どのIDで識別するかが設計事項になる。注文をDBからプログラムへ読み込むときは、Orderレコードの一件が何を表し、order_idをDBのどの注文IDと対応付けるかを決める必要がある。注文を別のサービスへ送る場合は、メッセージ、ログ、REST APIのパスでも同じ判断が必要になる。ファイルパス、LDAPエントリー、独自バイナリ形式を扱う場合は、階層、参照関係、レコード境界なども設計事項になる。

表現形式が異なっても、エンティティの名前、境界、同一性、粒度を定め、用途に必要な属性、関係、制約を割り当てる技術は共通する。具体的な表現には、DBの行、プログラムのレコード、APIのパスやメッセージ、ログイベント、ファイル内のレコードなどがある。

表現具体例設計事項
プログラムのレコードOrder{order_id,customer_id,status}一件の注文の境界、識別子、顧客との関係、状態
メッセージOrderCreated{order_id,occurred_at}一件のイベントの境界、参照する注文、発生時刻、形式のバージョン
ログファイルtimestamp,request_id,customer_id,event,result一件のイベントの粒度、処理を追跡するID、発生時刻、結果
ファイルパス/services/order-api/releases/2026-08-14/階層、ディレクトリ名、配置場所、名前を変更したときの参照方法
REST APIのパス/customers/{customer_id}/contracts/{contract_id}リソースの境界、親子関係、パスに含める識別子とその範囲
LDAPdnobjectClassuidmemberOfエントリーの識別、種類、必須属性、所属関係
独自バイナリ形式magic,version,record_length,payload,checksum形式の識別、バージョン、レコード境界、破損検出

各表現を設計する前に、システムで管理するエンティティへ名前を付け、その境界と同一性を定める。顧客注文サービスを管理する場合は、その名前を基準に、DBの行、プログラムのオブジェクト、通信のメッセージ、ログイベント、ファイル内のバイナリレコードで一件とする範囲を具体化できる。

同じエンティティに由来するデータを追跡するには、各表現のIDを対応付けられるようにする。顧客や注文を追跡する場合は、DBの行、APIのメッセージ、ログイベントにcustomer_idorder_idを記録する方法を選べる。

エンティティの命名と、境界、同一性、粒度の定義は構造化の出発点になる。

設計事項を整理するときは、三つの問いを使う。エンティティの定義、アプリケーションの仕様、ストレージへの配置のどの段階にあるか。同じエンティティについて、属性と関係、状態遷移、実行単位、送受信単位、保存配置、検索方法の何を決めているか。業務プロセス、データ、アプリケーション、実行基盤のどこに属するか。この三つを、それぞれ抽象度関心領域と呼ぶ。

各設計事項には、同一性の対応、時点、出自、バージョン、制約のうち、その表現を解釈するために必要な情報も記録する。これらが文脈である。

3.1 第1軸:抽象度

第1軸の抽象度には、概念、論理、物理の三段階がある。概念レベルでは、システムで管理するべき人、物や出来事を整理し、何を一つのエンティティとするかを決める。論理レベルでは、概念レベルで定めたエンティティを、アプリケーションが識別、検索、更新できるユースケースに基づいた仕様へ落とし込む。画面上のレコードやAPIメッセージは、論理表現の例である。物理レベルでは、論理表現をどのストレージへ配置し、どの単位で保存、検索、更新するかを、ストレージの仕様や製品特性などから決める。このように、構造化では三段階を行き来しながら、各段階でどのエンティティを扱い、どのような表現と配置が必要かを定義していく。この後の第4節では、それらについて詳しく説明する。

3.2 第2軸:関心

例えば注文システムにおいては、決められた注文エンティティの中からorder_idフィールドが何を識別し、total_amountフィールドが税込みか税抜きかを決めなければ一貫したシステムにならない。また注文と注文明細、顧客をどう関連付けるかを決め、受付済みから決済済み、出荷済みへ進む条件もルールを整理していく必要がある。決済処理をどのタスクで実行してどこから再試行するか、注文メッセージをどの単位で送るか、レコードをどこへ保存するか、order_idをどう検索するかも当然、検討・設計すべき事項になる。このように定めたエンティティがどのようにふるまうべきかどのように扱われるべきかを問うのが 関心 の軸である。

関心を分類すると、次のように対応する。

関心注文システムで決めること
意味/情報order_idが識別するエンティティと、金額や日時などの属性が表す内容
関係注文、注文明細、顧客、決済、配送の対応
振る舞い/プロセス受付、決済、出荷、取消しで発生するイベントと状態遷移
計算決済や在庫引当てを実行するジョブやタスクの境界、再試行する範囲
分散/通信注文データをメッセージ、ストリーム、セグメント、パケットとして送受信する単位
保存/配置注文レコードを格納するファイル、ページ、ブロック、パーティション、ノード
アクセス注文IDの完全一致、顧客別の一覧、期間検索に使うインデックス

注文のスキーマ、関係、状態遷移、タスク定義、メッセージ形式、保存配置、インデックスは、それぞれ別の設計成果物になる。関心は、同じエンティティについて何を決める設計なのかを区別する軸である。ISO/IEC/IEEE 42010:2022もアーキテクチャ記述の要件を定め、視点とモデル種別を使って関係者の関心を扱う。

3.3 第3軸:領域

注文を扱う場合、受注、決済、出荷からなる業務プロセスを整理する。注文、注文明細、取引履歴を管理するデータを定め、ユースケース、サービス、コマンド、APIとしてアプリケーションの仕様へ落とし込む。実行時には、プロセス、コンテナ、データベース、ネットワーク、ストレージへ配置する。

この四つの設計範囲を、それぞれビジネス、データ、アプリケーション、テクノロジーという領域に分ける。領域は、同じ注文を業務の流れ、管理するデータ、アプリケーション機能、実行基盤のどこから設計しているかを区別する。The Open Groupも、TOGAFとArchiMateの関係を説明する資料で四つのアーキテクチャ領域を挙げている。

3.4 分類を横断する文脈

複数の抽象度や領域にまたがるIDの対応、時刻データ、権限、データの入り口やプロセスの起点、バージョンや、フォーマット制約など様々な付帯情報を 文脈 として記録する必要がある。顧客を領域間で追跡する場合は、ビジネス領域で顧客にIDを振るルールが、データ上のエンティティ、APIの識別子、監査ログを対応付けるルールと統合、もしくは可換なルールでなければ識別できない。エラー発生時に誰がどの権限でのどのデータにアクセスしたか、すべての来歴を記録しておかなければデバッグは困難である。

4. 抽象度: 概念・論理・物理という三つの抽象レベル

概念レベルでは、現実にある人、物、出来事から、システムで管理するエンティティを定める。論理レベルでは、そのエンティティを機械で計算できる仕様へ落とし込む。物理レベルでは、論理表現を実際に読み書きする単位へ配置する。第4.1節から第4.3節で各レベルを詳しく説明し、第4.4節で同じエンティティを三段階に分けて比較する。第4.5節では、ユースケースを実現できるまで三段階をどのように行き来するかを扱う。

4.1 概念レベル:管理単位の名前と境界

概念レベルでは、これから実現するシステムが何を一つの業務として扱い、人、物、出来事をどの単位で管理するかを決める。属性や関係を持たせる入れものとして設ける管理単位が、要件定義や問題定義で扱う業務上のエンティティである。エンティティの境界、同一性、粒度、エンティティどうしの関係が設計事項になる。

管理単位には、その範囲を区別できる名前を付ける。管理単位の例には顧客法人企業グループがある。契約注文決済も別の単位を表せる。一方、取引データのように複数のエンティティを含められる名前では、注文、決済、返金のどれを一件と数えるのかが曖昧になる。名前と管理単位は、テーブル、API、メッセージ、ログから参照されるため、長期間維持できるよう慎重に決める必要がある。

管理単位を決めた後は、現実の帳票やシステム出力から得た属性と値をエンティティへ割り当てていく。冒頭の四つのCSVなら、株式会社アソピテック(株)アソピテックASOPI TECH Inc.という名称、各住所と電話番号を、情報源と観測時点を添えて法人へ保持する。特定の検索や集計で使うかにかかわらず、観測した値を事実として蓄積し、正規化した名称や同一性の推定結果とは分ける。別の帳票から法人番号や請求先住所が得られれば、同じエンティティへ新しい属性として追加できる。

概念レベルで管理単位を決める作業は、顧客管理、プログラム、ログ、ディレクトリ、独自バイナリ形式に共通する。たとえば顧客管理では、法人と企業グループを別のエンティティとして扱い、一つの法人が複数の契約を持つ関係を決められる。プログラムの処理待ちなら何を一件のジョブと数えるか、ログなら何を一件のイベントとして記録するかが設計事項になる。ディレクトリやLDAPでは人、組織、サービス、グループを管理単位として分ける場合があり、独自バイナリ形式では一件の計測値、画像、取引のどれをレコードとして保存するかを検討する。

概念レベルのエンティティは、属性が増えるたびに変更するものではない。顧客契約を例にすると、名前と管理単位を維持したまま観測値を追加し、用途ごとに名称や住所をどのフィールドへ割り当てるかを変えられる。テーブル名、フィールド、データ型、パス名、バイト位置は、次の段階で具体化する。

4.2 論理レベル:仕様とテストの粒度

論理レベルでは、概念レベルで整理した業務上のエンティティから、業務システムやアプリケーションが仕様上管理する主要なエンティティを定める。仕様を作るときは、たとえば識別子、属性、関係、制約、操作のうち、ユースケースに必要なものを検討する。プログラムが検証、検索、比較、集計、遷移できるよう、選んだ項目と操作を論理仕様へ落とし込む。

同じ観測事実から、用途に応じて異なる論理表現を作れる。顧客と契約を例にすると、customer_master(customer_id, legal_name, address)contracts(contract_id, customer_id, signed_at, amount)という二つのリレーションで表せるほか、顧客の中に契約への参照を持つドキュメントや、法人と契約をノードとエッジにしたグラフとしても表現できる。

論理表現はユースケースに応じて変わる。顧客ごとの契約を表示するなら、顧客と契約を別のテーブルへ分け、customer_idJOINする。顧客別の月次売上を集計するなら、契約からmonthcustomer_idtotal_amountcurrencyを選んだ集計ビューへ射影する。会社名で顧客を検索するなら、normalized_namecustomer_idを持つ名称検索用の表現へ射影する。エンティティの粒度を基準に、テーブル分割、結合条件、射影する属性を決める作業が論理レベルに当たる。

たとえば、会社名で顧客一覧を検索するユースケースでは、次のような論理仕様を記述できる。

入力: name_contains = アソピテック
並び順: legal_name ASC
返す項目: customer_id, legal_name, address

この例では、検索文字列、並び順、返すフィールドが利用者から観測できる仕様になる。並び順の要件がなければ、legal_name ASCは論理仕様に含めない。検索と並べ替えをSQL、アプリケーション内の処理、検索基盤のどこで実行するかは、性能やデータ量に応じて物理レベルで選ぶ。

論理レベルで定めたエンティティの粒度は、システムテストのシナリオ、テストデータ、期待結果を記述する基準になる。顧客と契約を別のエンティティにした場合、契約登録のシステムテストは次の単位で記述できる。

前提: 顧客 C-001 が登録されている
操作: 顧客 C-001 の契約 K-1001 を登録する
期待: 契約 K-1001 が一件作成され、顧客 C-001 から参照できる

契約金額の変更、契約状態の遷移、顧客ごとの契約検索も、契約顧客の単位で前提と結果を表す。仕様上のエンティティとテストの粒度をそろえると、どのエンティティが作成、更新、検索されたかを一貫して検証できる。

処理待ちのジョブの論理仕様には、識別子、属性、操作、状態遷移を含められる。たとえばjob_idを識別子とし、statepriorityretry_countscheduled_atを属性として持たせる。ジョブの投入、取消、再試行、優先順位の変更と、その結果として許される状態遷移も記述する。

ジョブの配置先は物理レベルで決め、永続性、処理順序、同時実行数に応じてキューやストレージを選ぶ。

4.3 物理レベル:配置とアクセス方法

物理レベルでは、論理表現をメモリ、ファイル、ストレージ、ネットワーク上の具体的な単位へ配置する。設計項目には、たとえば読み書きの単位、バイト順、境界調整、圧縮、パーティション、複製、インデックス、キャッシュ、ノード配置がある。どの項目を検討するかは、性能、容量、鮮度、障害回復、再生成時間の要件によって変わる。

保存先は、後続処理のアクセス方法から選ぶ。注文や決済の状態へランダムアクセスし、短いトランザクションで参照と更新を繰り返すならRDBMSが適している。PostgreSQLもMVCCによって同時アクセス時の整合性を管理する

アプリケーションログやJSONを一度または低頻度で書き込み、後からファイル単位で取得または一括処理するなら、Amazon S3のようにバケットとキーでオブジェクトを識別するストレージを置き場にできる。

JSONログを大規模に検索・加工する場合は、未加工のログをS3へ置き、検索や集計に使う属性を検索基盤や分析基盤へ射影する。大量のログをトランザクション処理用のRDBMSへ入れて繰り返し走査するより、保持と検索を別の物理配置へ分ける方がユースケースに合う。

ユースケースからは、論理エンティティの属性に加えて、物理配置の要件も導く。同じ論理表現でも、後続処理がトランザクション付きのランダムアクセスを要求すればRDBMSへ配置し、低頻度で書いたデータをファイル単位で扱うならS3へ配置する。物理レベルまでユースケースに応じて検討することで、RDBMSをすべてのデータの既定の保存先にする設計を避けられる。

物理レベルで決める項目は、保存形式によって異なる。たとえば独自バイナリ形式では、マジックナンバー、バージョン、バイト順、フィールドのオフセット、レコード長、チェックサムのうち、読み取りと互換性に必要な項目を決める。DBでは、ユースケースに応じてB-treeインデックス、ページ、パーティション、行指向と列指向、レプリカの配置を検討する。論理レベルで同じフィールドを持つレコードでも、物理配置によって読み書きの回数と復旧方法が変わる。

4.4 同じエンティティを三段階で比べる

三段階の違いを、同じエンティティで比較すると次のようになる。

概念レベル論理レベル物理レベル
顧客と契約法人、企業グループ、契約という管理単位顧客・契約エンティティ、属性、関係、一覧の並べ替えと絞り込みRDBMS、検索インデックス、キャッシュ
処理待ちのジョブジョブと実行試行という管理単位、再試行との関係ジョブの識別子、状態、優先順位、投入・取消・再試行メッセージ基盤、RDBMS、メモリ上のキュー
ログイベントとリクエストという管理単位、その発生順序時刻、リクエストID、イベント種別、検索・集計条件オブジェクトストア、検索基盤、列指向ファイル

論理レベルは、エンティティのフィールドと関係に加えて、利用者や後続処理が実行する操作を表す。顧客一覧の並べ替えやジョブの再試行は、要件に含まれる場合に論理仕様として記述できる。物理レベルでは、その操作をどこで、どのアクセス方法によって実行するかを決める。

4.5 三つのレベルを往復する設計

概念、論理、物理の三つのレベルは、ユースケースを実現できるか確かめながら往復する。概念レベルで業務上のエンティティと観測事実を整理し、論理レベルで仕様上のエンティティを定め、その粒度でシステムテストを記述する。物理レベルでは検索、更新、保存を実装し、応答時間や書き込み性能を測定する。テストで管理単位の不足が分かれば概念レベルへ戻り、性能要件を満たせなければ論理表現や物理配置へ戻って設計を修正する。

たとえば顧客という一つのエンティティに法人と企業グループを入れると、企業グループ単位の売上集計には使えても、法人単位の契約と請求を一意に管理できない。この場合は概念レベルへ戻り、法人企業グループを別の管理単位として命名し、両者の所属関係を定める。論理レベルではそれぞれのIDと関係をレコードの仕様へ落とし込み、物理レベルでは検索と集計に使うインデックスを作り直す。

物理レベルの選択は、読み書きの頻度と処理に許容される時間によって変わる。一件のオブジェクトを一度だけ書き、後から一括処理するならオブジェクトストアへ保存する構成を選べる。注文や決済を高頻度に更新し、トランザクションを必要とするならRDBMSが候補になる。検索結果を0.5秒以内に返す要件があれば、検索用インデックスや事前計算した射影を検討する。完了まで3時間を許容できるバッチなら、正本や列指向ファイルを走査する構成も選べる。測定結果が要件を満たさなければ、ストレージとアクセス方法だけでなく、検索や集計に使う論理表現まで戻って見直す。

新しい観測値や属性は、管理単位を保ったままエンティティへ追加できる。論理表現でフィールドを増やす場合や、物理配置でインデックスを変える場合は、概念レベルのエンティティを維持できる。ユースケースが管理単位と合わない場合に概念レベルまで戻ることで、エンティティの名前と境界を安定させながら、システム全体の設計を反復できる。

全文検索と通常の条件検索を一つの画面で組み合わせる場合、どのフィールドを全文検索インデクスへ入れ、二種類の検索クエリをどう組み立てるかは、採用する製品やDBによって変わる。検索結果の精度と応答時間、画面の使いやすさを実装で確かめ、論理レベルの検索仕様と物理レベルのインデクスを往復しながら決めていく。

5. 一つのエンティティ、複数の射影

一つのエンティティに対する複数の射影は、抽象度関心領域の三軸で位置付け、各射影の文脈を記録して設計する。

各射影の属性と演算は、ユースケースから決める。たとえばID検索、全文検索、関係探索、集計、画面表示では必要な属性と演算が異なるため、各表現の正本と生成方法も個別に記録する。すべてを一つの表現形式へまとめると、用途ごとに不要な属性や処理が入り、どの用途にも合わない中間形になりやすい。

同じ顧客から作る複数の射影では、法人の同一性、住所の観測時点、契約の有効期間、各値の情報源を共通の文脈として記録する。各射影に同じ顧客IDと時点条件を適用し、正本のフィールドと派生データのフィールドを対応付ける。

ただし、一つのエンティティに対して複数の表現を設計することは、方式の乱立を容認するものではない。たとえば名称検索用のcustomer_name_index.csvへ住所や契約額まで加え、月次集計用のcustomer_monthly_sales.csvへ企業関係や画面表示用の文言まで加えると、同じ顧客情報の複製と更新経路が増える。各ファイルを別々に更新すれば、会社名、売上、所属関係のどの値が正しいか判断できず、正本から再生成したときにどの変更が消えるかも分からなくなる。このような乱立を避けるには、各射影が表現する関心を名称検索、月次集計、関係探索などに絞り、その射影で実行するクエリ、保持する属性、正本、再生成方法を対応付ける必要がある。

三軸による分類と文脈は、設計中の問いがどこにあり、どのデータを根拠にするかを確認するために使える。たとえば同じ企業グループの契約をまとめて表示したいというクエリには、少なくとも次の設計対象が関わる。

設計対象三軸による分類記録する文脈
法人と企業グループを別の管理単位にする判断データ領域/情報の関心/概念レベル同一性の規則・判断に使った情報源
法人間の所属関係データ領域/関係の関心/論理レベル関係の有効期間・情報源
企業名による候補検索データ領域/アクセスの関心/物理レベル正規化規則・インデックスの生成時刻
契約総額の集計ビューデータ領域/情報の関心/論理・物理レベル集計期間・入力データ・集計規則のバージョン
画面に返すレスポンスアプリケーション領域/情報の関心/論理レベル顧客ID・参照時点

この表の各行は別々の設計成果物を持てる。法人と企業グループを別の管理単位にする段階では、両者の名前、境界、所属関係を決める。グラフDBやインデックスの種類は、論理表現とユースケースを確認した後の物理設計で選ぶ。所属関係を論理グラフとして表しても、保存先はリレーションにできる。企業名検索のインデックスと契約集計ビューは、同じ正本から作られる別の物理的な射影になる。

成果物を一つのクエリへ組み込むとき、どのID、時点条件、情報源を使うかは、クエリと各成果物の生成元によって変わる。たとえば、この表の成果物から法人単位の有効契約を表示するなら、所属関係、検索結果、集計ビュー、レスポンスを顧客IDで対応付け、契約の有効期間を集計条件に使う構成を選べる。企業グループを推定した結果まで表示するなら、観測した法人情報と推定規則の情報源を区別して記録すると、推定の根拠を追跡できる。

構造化では、システムが管理するエンティティに名前を付け、境界、同一性、粒度、関係を決める。ユースケースごとに使用する属性と演算を選び、論理仕様へ落とし込む。さらに、物理配置、アクセスパス、分析用テーブル、表示用ビューを設計する。必要な検索結果や集計結果を生成できない場合は、管理単位、論理表現、物理配置の定義を確認し、該当する抽象レベルの設計を更新する。


この回ではエンティティを定め、抽象度の軸について解説し、用途別の論理仕様へ落とし込んで物理配置を決めるところまでを整理した。次回は、関心と領域から、関係、振る舞い、通信、保存を整理する。