みなさん、データモデリングしてますか?
こんにちは。大阪でSalesforce基盤を使ったシステム開発に従事している佐野です。Salesforceを使うと、導入型のシステム開発と比べると驚くほど簡単に魔法のように早く開発出来ます。GUIでブラウザからオブジェクト(≒テーブル)を定義すると画面が自動的に出来るだけでなく外部とのSOAPインターフェースも自動的に出来ます。
とは言え少し複雑な業務を設計する時にはしっかりデータモデリングしてから開発する事が重要です。読者の中には「データモデルなんて過去のもの、今から学ぶならオブジェクト指向アプローチ(OOA)かドメイン指向設計(DDD)では?」と思っている人も多いのではないでしょうか。しかしそれは誤解です。プログラム設計/クラス設計の時にはOOAを使いますが、業務設計はRDBで構築する事を前提とする限りデータ・モデリングが最適です。「帳簿組織を設計する」という感覚が重要なのですがそれは今回のテーマではありませんので触れません。
開発基盤としてSalesforceを選ぶならデータモデリングで業務設計するしかありません。そこで筆者が実践しているデータモデリングの手法をさらっと説明した後に、Salesforceでどうやって実装しているのかを説明します。(これらは基盤だけでなく業務に踏み込んだ話ですのでプロジェクトごとに変化します。あくまでも一つの例だと捉えてください。)
データモデリングって何?
筆者が新人の時には、まだ日本にリレーショナルデータベース(RDB)はありませんでした。日本ORACLEが1985年に設立され、1990年ごろから広がり始めました。2000年頃にはRDB以外のデータベースは新規では使わなくなりました。RDBの原点は当時IBMのE.F.Codd博士の1970年の論文です。それまでのデータベースはポインタを使ってアクセス経路の構造を作っていましたが、「純粋にデータだけを見て関係がわかる」全く新しい理論に基づいた論文でした。この辺り興味のある方は次のブログをご覧下さい。
RDB以前のシステム設計の手法は主に業務の過程や手順に着眼して設計を行うPOA(Process Oriented Approach)でした。このRDBが広がると共に、処理よりもデータが変化に強い(変り難い)特性を生かしてデータとデータ間の関連に着目するデータモデリングを基本に設計する流れが生まれました。この手法は日本ではDOA(データ中心アプローチ)と呼ばれて独自の進化を遂げています。英語のDOAはドラマ「ER」などで出てくるDOA(Dead On Arrival=来院時心肺停止 状態)という意味になりますのでこれは日本語英語です。米国では情報工学(InformationEngeneering : IE)や情報資源管理(Information Resource Management:IRM)と呼ばれていますが、日本の流派ほど細かく分析はしません。
設計するためのツールは?
Salesforceにはsummer'12から正式リリースになった「スキーマビルダー」というツールがあります。このツールを使うことで画面上からドラッグ&ドロップの作業でオブジェクト(テーブル)や項目を作ることが出来ます。
スキーマビルダーで描画すればそのまま実装も出来るところは大変便利ですが、データモデル構造だけをシンプルに見たいという用途には向きません。そこで筆者はオープンソースのモデリングツール「X-TEA(エクスティ) Modeler」を使用しています。モデリングツールは何種類か試しましたが、1:Nの関係を親子関係と参照関係で明示的に表現出来る事と、項目に「更新不可属性」を設定出来る(何故必要かは後述)ためこのツールを重宝しています(作者に感謝)。このデータモデルツールの特徴はジェームズ・マーチン氏のIE(information engineering)表記法(俗に「鳥の足」とも呼ばれます)を横長に表記してある事です。他のツールでも同じ表現は出来ますので使い慣れたツールをお使い下さい。
では、上と同じデータ構造をX-TEAで見てみましょう。
顧客・顧客担当者・商談だけを関係付けたデータモデルです。「顧客」と「顧客担当者」の間は鳥の足のような特徴的な線で結ばれています。これが親子関係を表します。「顧客担当者」と「商談」の間は先が点々になっています。これは同じ1:NでもN側にNullを許す参照関係を表しています。この2つの書き分けが出来るツールは珍しいのでSalesforceに向いています。
顧客オブジェクトの先頭の項目「011-id」は顧客オブジェクト(011)のSalesforceIdを示しています。
SalesforceIdとはオブジェクトの各レコードに一意に付けられる番号です。この値を見れば、会社の中でレコードを1つに特定出来ます。一般に言う「オブジェクトキー」とほぼ同じです。Salesforceはこの値をURLに指定するとレコードを検索出来るという奇跡のようなURI設計(笑)になっています。
お客様との会議に使う時は実データ(インスタンス)を表示するとお客様はよりイメージが湧きデータ構造についての議論が楽になります。我々は「顧客名」というメタデータで議論する事に慣れていますが、地に足がついた議論をお客様とする時は実データで会話する事をお奨めします。例えば「テラスカイ商事」と話をするとお客様から「営業所が10箇所あるから・・・」「2箇所の営業所に所属している人も居て・・」というような情報を得る事が出来ます。メタデータで抽象的な議論をしている限りこういう情報を引き出すのは難しいです。
※Salesforceでは親子関係の事を主従関係と呼んでいます。これは英語のMaster-Detail Relationshipsの正確な訳語ですが、親子関係と表現する事もありますのでご了承下さい。
筆者個人はこの方式で書くことも多いのですが、実はこのデータモデルには少し誤魔化しがあります。Salesforceのフレームワークはすべてのオブジェクトの主キーはSalesforceIdなのです。このモデルは複合主キーとして表現しているため正確ではありません。とは言え主従関係の項目はNULLを許さない必須項目になりますし、子レコードを作成後に別の親レコードに変更出来ない更新不可属性も付きますので実質は複合主キーと同じです(変更可能にする事も出来ます)
このとおりSalesforceのデータ構造はER(EntityRelationship)構造を強く意識した機能が多くありますので業務システムを構築しやすくなっています。
本当に複雑なデータモデルの時に主キーも正確に表したい時は、外部キー(foreign key)を使います。複合主キーと外部キーの違いはNotNull制約と更新不可制約だけです。
- 主キーはSalesfordeIdの単独主キーとする
- 複合主キーとしたい他の項目はNotNULL制約+更新不可制約を付ける
(このためにX-TEA Modelerの「更新不可属性」が必要になります)
X-TEA Modelerの特徴的な鳥の足が表現できないのですが、これで誰からも後ろ指を刺されないデータモデルとなりました←誰も刺しません(笑)
Salesforceのリレーションのたどり方
普通のRDBのSQLではテーブル間で同じ項目があれば表を結合する事が出来ますが、SalesforceのSOQLでは結合という構文がありません。その代わりオブジェクト間に予めリレーションを設定することでリレーションをたどる事が出来るようになります。E.F.Codd博士が「純粋にデータだけを見て関係がわかる」事を目的としてRDBを発明されましたが、RDBの場合同じ「顧客コード」がAテーブルとBテーブルで同じ意味を持っているのか明白ではありません。Salesforceは明示的に関係を項目として書いてしまいますのでRDBよりも「データだけを見て関係がわかる」という目的に合ってるのかも知れません(冗談です)
次の4つのカスタムオブジェクトを定義します。
- 親オブジェクト:MDtest01を作成し、カスタム項目TextFieldを定義します
- 子オブジェクト:MDtest02を作成します。テキスト項目:TextFieldと、MDtest01オブジェクトへの主従関係項目::MDtest01を定義します。
子リレーション名はMDtest02sとします
※子リレーション名は子オブジェクト側で定義しますが、親から子を見る時に使いますのでモデル上は親側に( )で定義します。
子オブジェクト名の複数形とすることが一般的です - 同様にMDtest02を親オブジェクトとした子オブジェクト:MDtest03を作成します。
- 同様にMDtest03を親オブジェクトとして子オブジェクト:MDtest04を作成します
子オブジェクトから親オブジェクトを参照する方法
子オブジェクトから親オブジェクトへは「リレーション名」を使ってあたかも子オブジェクト内の項目かのように参照することができます。カスタムオブジェクトのリレーション名は、主従(参照)関係項目名+__rとなります。
SOQLのFrom句には子オブジェクトしか指定しませんが、最大5レベルまで(主従/参照の)親のリレーションをたどる事が出来ます。
//MDtest04をベースのオブジェクトとして、親を辿るコード Select a.TextField__c, b.TextField__c, c.TextField__c, d.TextField__c From MDtest04__c a, MDtest04__c.MDtest03__r b, MDtest04__c.MDtest03__r.MDtest02__r c, MDtest04__c.MDtest03__r.MDtest02__r.MDtest01__r d
親オブジェクトから子オブジェクトを参照する方法
親オブジェクトから子オブジェクトを参照する時には、子リレーション名を使用したサブクエリを使用します。上記のデータモデルでは子リレーション名を( )で表示しています。
// MDtest02から子オブジェクトのMDtest03を参照する時 Select a.TextField__c, (Select b.TextField__C From MDtest03s__r b) From MDtest02__c a
ここで大きな制限があります。ベースとなるオブジェクトの子オブジェクト1つだけ、たどる事が出来ます。子の子や親の子にアクセスする事は出来ません。
1:Nの関係の1からNをアクセスしていますので結果は複数行になります。子の子のアクセスだと複数行の結果に対して複数行をアクセスする事になりますからプラットフォームのリソースを使いすぎるためだと推測しています。将来的には多少緩和される事を期待しています。
親:5レベル、子1レベルの範囲であれば1つのクエリで同時にアクセスが出来ます。
多対多のリレーション
業務モデリングに慣れていない方はオブジェクトとオブジェクトの関係が多対多になっても気にされない事があります。例えば、商談に複数の商品が紐付く場合、商談オブジェクトと商品オブジェクトは多対多になります。
業務モデリング的には多対多になるという事はまだ分析が不十分だとみなします。オブジェクト指向系のモデリングツールを使用されると多対多が描けてしまいますのでご注意下さい。Salesforceでも無理やり多対多のオブジェクトを設定する事は出来ますが業務的に使えないシステムになります。その時の解決策として、「連結オブジェクト」という特殊なオブジェクトが用意されています。2つの主従関係を持つだけであり、難しくありませんのでドキュメントをご覧下さい。商談と商品の間に商談商品という連結オブジェクトを持ちます。
ただ、直接の関係と比べるとレポートなどに制限が出ます。非正規形にすることで連結オブジェクトを避ける方がSalesforceの標準機能を多く使える事も多いです。お客様にとってどちらの方がメリットがあるかをしっかり議論しましょう。
正規化とSalesforceの制限
RDBの数学的な基礎は正規形(normal form:NF)です。様々な学者が様々な正規形を発明(?)発見(?)されていますが、実務者としては横目で見る程度で良いだろうと思います。どこまで厳格に実装するかはシステム次第ですしエンジニアの腕の見せ所です。正規形の目的は更新異常を防ぐ事です。
「One Fact In One Place」(1つの事実は1つの場所に)という言葉を聞かれた事があるでしょうか?それを目的と勘違いされているエンジニアを見かけますが、目的は更新異常を防ぐ事です。または更新異常が発生しても業務に影響のないように設計しましょう。更新異常が発生しないように入力規則・ワークフロー・トリガーなどを駆使しましょう。
正規形についてhttp://sasurahi.up.seesaa.net/docs/HigherNormalization.pdf から引用します。BCNFより上はNullを防ぎ、更新異常を防ぐ事を目的とし過ぎて業務的な関係を保持出来なくなっています。SalesforceはNullは怖くありませんのでそれらの考慮は不要です。
Normal Form | 排除したい状態 | 定義した人 |
第1正規形(1NF) | 繰り返し項目 | E.F.Codd(1970),C.J.Date(2003) |
第2正規形(2NF) | 部分関数従属 | E.F.Codd(1971) |
第3正規形(3NF) | 推移的関数従属 | E.F.Codd(1971) |
BC正規形(BCNF) | 非キーからキーの一部への関数従属 | Raymond F.Boyce and E.F.Codd(1974) |
第4正規形(4NF) | 対象性のある多値従属性 | Ronald Fagin(1977) |
第5正規形(5NF) | 自明でない結合従属性 | Ronald Fagin(1979) |
ドメイン/キー正規形(DKNF) | 静的な値に隠れたドメイン制約 | Ronald Fagin(1981) |
第6正規形(6NF) | 複数属性 | C.J.Date,Hugh Darwen,and Nikos Lorentzos(2002) |
<制約と解決方向>
※参考:ドキュメント リレーションの考慮事項
1.名称マスタ的なものは「選択リスト値セット(グローバル選択 リスト)」
小さなオブジェクトを作成すると使い勝手が悪くなります。
選択リスト値セットはオブジェクトではありませんが、使い勝手が
めったに変更されないもので、1000件以内なら使えます。
2.1つの子→親リレーションに指定できるレベルは5つ以下
MDtest04からMDtest01の項目を参照するのは3レベルです。
MDtest03__r.MDtest02__r.MDtest01__r.TextField__C
参照も含めて5レベルまでしか辿れません。必要な時は途中のオブジェクトに中間結果の数式を持ちます。
3.カスタムオブジェクトには最大25個のリレーションが使えます
将来の機能追加を考えると15個程度で収まるように設計します。
実装は正規形にこだわらず、更新異常を防ぐ事が出来ているかを意識します。
4.主従関係を設定出来るのは最大3レベルまで
4つ目の主従関係を設定しようとしても出てこなくなります
01→02→03→04と主従関係があるとき、05の主従関係には04のオブジェクトが出てきません。
どうしても必要な時は、04の中に05の内容を横持ち(1NF違反)するか親を03とします。
5.親→子のリレーションは1つのレベルしか1つのクエリでは指定出来ません
どうしても必要な時は非正規化するなど工夫して解決策を探します
親→子1→子2で親から1クエリで子2までアクセスしたい時
例1:子1にトリガを作り子2の中の必要な項目を子1の項目として持たせる
(削除について考慮する必要のないときだけ使える手です)
例2:親→子2という参照関係も同時に作り、検索後子1のものだけ選択する
他にも方法はあります
6.カスタムオブジェクトで作成出来る主従関係は2つまで
参照関係は多数設定出来ますが、主従関係は2つまでです。
3つのオブジェクトの連結オブジェクトは作成出来ません。
7.多対多の自己参照は作成出来ません
連結オブジェクトの2つの主従関係に同じ主オブジェクトを設定出来ません。
自己参照関連では細かい制約を見過ごして設計見直しする事がたまにあります。
実際のデモ組織で設定出来るかを調べながら設計しましょう。
今日はここまで。これからも明るくデータモデリングしましょう!