ケーススタディ · プラットフォームエンジニアリング。Meta Dados メソドロジー(Genba · Kanso · Omotenashi)。
要旨。 サービス業ドメインのマルチテナントSaaSでは、同一の人間が顧客・従業員・オーナーとして現れる — ときには複数の店舗にまたがって。私たちはこの問題をアンカー付きアイデンティティのグラフとしてモデル化する:グローバルなエンティティ(CPFでアンカリングされた「人」)に、テナントごとに非正規化された結びつきが収束する。本番データベース上でROLLBACKを伴うトランザクション内で実行した制御実験により、アイデンティティはCPFによって正しく統合される一方で、非正規化データがテーブル間で気づかれないまま乖離すること、そしてCPFがnullのまま「インライン」でログインを作成する一群のエントリポイントが一人の人間を二つのアイデンティティに分裂させることを示す。検証済みの二つの介入策を記述し、再利用可能な原則を導出する。
1. はじめに
予約プラットフォームは三つのエンティティを第一級市民として扱う — 顧客・従業員・店舗 — それぞれが固有の自然キーを持つ:顧客は電話番号で識別され、従業員はCPFを要求され、店舗はCNPJ + メールアドレス(個人事業主の場合はCPF)で識別される。問題が生じるのは、この三つが互いに素ではないからだ:顧客が雇用されて従業員になることがあり、顧客が自ら事業を立ち上げることがあり、オーナーが別のサロンで顧客として予約することがある。システムは、情報がテーブル間を行き来し、一貫性を保つこと、そして異なる登録において重要データが乖離しないことを必要とする。
本稿ではMarcaêプラットフォームをケーススタディとして扱うが、モデルと知見は一般的なものである:個人が時間とテナントをまたいで異質な役割を担う、あらゆるSaaSに当てはまる。
2. モデル:アンカー付きアイデンティティ
中心的な考え方は、アイデンティティを役割から分離することである。人はグローバルなエンティティ — テナントを持たず、強力なキー(正規化されたCPF)でアンカリングされる。各結びつき(顧客、従業員、ユーザー)は重要フィールドの非正規化されたコピーと、その人を指す外部キーperson_idを持つ。データベーストリガー(vincular_pessoa_por_cpf)がfind-or-createを行う:有効なCPFを伴う結びつきを書き込む際、そのCPFの人を関連付ける(あるいは作成する)とともにperson_idを固定する。有効なCPFがなければ、アイデンティティはテナントローカルとなる。
3. 不変条件と乖離メトリクス
モデルは二つの不変条件を保証しなければならない。第一はアイデンティティの統合(I1):同じ有効なCPFを持つ二つの結びつきは同一の人を指す。第二は役割の完全性(I2):人は自らの結びつきから導かれるすべての役割(テナント + 役割)を把握している。
重要データ(氏名、メール、電話)の一貫性を測るため、乖離率 δ を定義する:アンカーとその結びつきの間で、いずれかの重要フィールドが空でない値を複数持つ人の割合。δ = 0 が目標である — 人が唯一の真実の源となる。
4. アイデンティティ分裂という欠陥
一群のエントリポイントは、CPFを無視するアンカリングを用いてログインアカウントを「インライン」で(顧客と同時に)作成する — 実際には、常に新しい人を作成してしまう。ある人間がアンカリングするエントリポイントとアンカリングしないエントリポイントの両方を通過すると、その人間は二人の人として表現されるようになり — I1に違反する。分裂率 σ は、アンカリングしないエントリポイントの使用に比例して増大する。これは最も深刻度の高い欠陥である。なぜなら、いったん分裂すると、アイデンティティは手動/SQL操作によってしか和解できないからだ。
5. 方法論と結果
私たちはGenba(現場に赴く)を採用した:意図されたモデルについて推論するのではなく、実際のモデルを動かして検証する。二つの技法を用いた:(1) 顧客/従業員/ユーザー/オーナーが生まれ、あるいは変化する15のエントリポイントを網羅的に読み解き、それぞれが何を書き込み、CPFでアンカリングするか否かを記録する;(2) トランザクショナルシミュレーション — アイデンティティのジャーニーを本番データベース上で直接、BEGIN … ROLLBACKの内側でインスタンス化する:アンカリングトリガーは実際に発火するが、何も永続化されない。
結果はモデルを裏付けるとともに、それが失敗する箇所を浮き彫りにした:
- アンカリングがある箇所ではI1が成立する — 2テナントで5つの役割を巡る一人の人は同一のperson_idを記録した。
- 重要データは乖離する — アンカーは最初に見たものだけを保持する:従業員が後から受け取ったメールは決して人へ反映されず、氏名はテーブル間で乖離した。
- 制御されたシナリオではσ = 0.5 — 2人の人間のうち1人が、インラインでログインを作成するエントリポイントによって二つのアイデンティティに分裂した。
6. 介入策
同値関係としての重複排除。 旧ルールは電話番号だけで二つの登録を統合していた;新ルールは正規化された電話番号かつ氏名を要求する。こうして、電話番号を共有する別々の人間(自分の携帯電話で子どものために予約する母親)が誤って統合されることがなくなる。
フィールド単位のfoldとしてのマージ。 二つの登録が実際に同一人物である場合、統合はもはや「一方を選んで他方を破棄する」ものではなく、フィールドごとの解決となる:差分テーブルが乖離した各フィールドを並べて提示し、オペレーターが生き残る側に残す値を選ぶ。順序が重要だ — 選択を適用する前に参照を張り直し、削除される側をアーカイブする — さもなければ部分ユニークインデックスが衝突する。データを失うマージはリグレッションであり、フィールド単位のマージは各登録の最良の部分を保存する。
7. 考察と原則
本ケースは繰り返し現れるトレードオフを示す:結びつきに重要フィールドを非正規化すると、各画面は高速かつ自律的になるが、一貫性が犠牲になる;人を唯一の真実の源として維持すると、乖離はなくなるが、読み取り時の結合が犠牲になる。共有アイデンティティを持つあらゆるマルチテナントプラットフォームのための、再利用可能な原則:
- 一つのアンカー、一つのキー。 グローバルなエンティティと強力なキー(CPF/CNPJ)を選び、すべてのエントリポイントにそれでアンカリングさせる。nullでアンカリングするエントリポイントが一つでもあれば、保証は崩れる。
- アンカリングは役割ではなく状態の不変条件である。 役割を状態としてモデル化する;顧客→従業員→オーナーの遷移はperson_idを保存しなければならない。役割を「変える」とは結びつきを追加することであり、決して新しいアイデンティティを作成することではない。
- 一貫性はフィールドごとに決める。 メールと電話は唯一の源を求める;ローカルな備考は結びつきに存在してよい。メトリクス δ が各決定のコストを測る。
- 重複排除は同値関係である — 明示的に選べ。 「同じ電話番号」と「同じ電話番号かつ氏名」は異なる分割を生む。
- 和解はfoldであり、破棄ではない。
8. 結論
MarcaêのアイデンティティはCPFがあるときには正しく統合される — アンカーは機能し、役割の遷移に対して頑健である。アイデンティティを分裂させていた一群のエントリポイントは、データベースのトリガーによって封じられた(和解によりアカウントがCPFの人に追従することが保証され、マージが孤立したログインを生成しなくなり、アンカーが欠けていた重要データを学習するようになった)。残るのは完全な唯一の真実の源のみ — これは設計上の判断であって欠陥ではない。得られたものは技術的なだけではない:それは従業員になる顧客も、顧客になるオーナーも、システム全体において同一人物であり続けるという保証である。
よくある質問
なぜ人を識別するのに電話番号だけを使わないのですか?
電話番号は人の強力なキーではないからです:別々の人間が一つの番号を共有し(自分の携帯電話で子どものために予約する母親)、また一人の人が番号を変えることもあります。CPF(強力なキーであり、個人ごとに一意)でアンカリングし、電話番号+氏名は重複排除の関係としてのみ使うことで、異なる人間を誤って統合することを避けられます。
これは予約サービスだけに当てはまるのですか、それともあらゆるSaaSに当てはまるのですか?
モデルは一般的なものです。同一の個人が時間とテナントをまたいで異質な役割を担う、あらゆるマルチテナントSaaSに当てはまります — マーケットプレイス、医療プラットフォーム、教育、マンション管理など。「一つのアンカー、一つのキー;役割は状態であってアイデンティティではない」という原則はドメインをまたいで転用できます;変わるのは強力なキーと重要フィールドの集合です。