CRDTとは?Conflict-free Replicated Data Typeの仕組み・用途・注意点を解説
CRDTとは、Conflict-free Replicated Data Typeの略で、日本語では「コンフリクトなし複製データ型」または「競合なし複製データ型」と訳されます。複数の端末、サーバー、ブラウザ、アプリケーションインスタンスに同じデータのコピーが存在し、それぞれが独立して更新されても、最終的に同じ状態へ収束できるように設計されたデータ構造です。特に、リアルタイム共同編集、オフラインファーストアプリ、Local-first Software、P2P同期、分散データベースなどで重要な考え方です。
従来の同期では、複数のユーザーが同じデータを同時に編集すると、どちらの変更を優先するか、どちらを破棄するか、ユーザーに手動で選ばせるかといった問題が発生します。CRDTでは、データ型そのものにマージのルールを持たせることで、複数の更新を自動的に統合し、各レプリカが最終的に同じ状態になるようにします。つまり、CRDTは「競合が起きない魔法」ではなく、「競合が起きても、定義された規則に従って自動的に収束できるようにする技術」です。
たとえば、ユーザーAがオフラインで文書を編集し、同時にユーザーBも別の場所で同じ文書を編集したとします。通常の保存方式では、後から保存した内容が前の内容を上書きする可能性があります。しかしCRDTを使うと、ユーザーAとユーザーBの変更を後からマージし、両方の変更をできるだけ失わずに統合できます。これにより、ネットワークが不安定な環境でも、ユーザーはローカルで作業を続け、後から自然に同期できる体験を作りやすくなります。
1. CRDTとは
CRDTとは、複数の場所に複製されたデータが、それぞれ独立して更新されても、最終的に同じ状態へ収束するように設計されたデータ型です。通常のデータ型では、同じ値に対して同時に異なる変更が行われると、どちらを採用するかを決めなければなりません。CRDTでは、このような競合をデータ型の性質として扱い、更新の順序が異なっても最終的に同じ結果になるようなマージ規則を持ちます。
CRDTの大きな特徴は、中央サーバーが常に唯一の正解を即時に決める必要がないことです。各レプリカはローカルで変更を受け入れ、ネットワークがつながったタイミングで他のレプリカと同期できます。これは、クラウドに常時接続できないモバイルアプリや、複数人が同時に編集するドキュメントツールにとって非常に重要です。ユーザーはサーバー応答を待たずに操作でき、システムは後から変更をマージできます。
| 項目 | 内容 |
|---|---|
| 英語表記 | Conflict-free Replicated Data Type |
| 略称 | CRDT |
| 日本語表現 | コンフリクトなし複製データ型、競合なし複製データ型 |
| 主な目的 | 複数レプリカのデータを自動的に収束させる |
| 関連概念 | 最終的整合性、強い最終的整合性、レプリケーション、同期 |
| 主な用途 | 共同編集、オフラインファースト、分散DB、P2Pアプリ |
| 代表的ライブラリ | Automerge、Yjs |
1.1 複数のレプリカを前提にする
CRDTは、データが一つの場所だけに存在するのではなく、複数のレプリカとして存在することを前提にします。レプリカとは、同じデータのコピーのことです。たとえば、同じメモ文書がユーザーAのPC、ユーザーBのスマートフォン、サーバー、別のブラウザタブに存在する場合、それぞれがレプリカになります。各レプリカは一時的に異なる状態になることがありますが、同期が進むと最終的に同じ状態へ近づきます。
この考え方は、現代のアプリケーションにとって非常に自然です。ユーザーはスマートフォン、PC、タブレットなど複数の端末を使います。さらに、複数人で同じデータを編集することもあります。ネットワークが常に安定しているとは限らないため、サーバーと接続できない間もローカルで編集できることが求められます。CRDTは、このような複数レプリカ・複数ユーザー・不安定ネットワークという現実に対応するための技術です。
1.2 同時更新を許可する
CRDTでは、複数のレプリカが同時に更新されることを前提にします。これは、従来のロック方式とは大きく異なります。ロック方式では、誰かが編集している間は他の人が編集できないようにすることがあります。これは整合性を保つには分かりやすい方法ですが、共同編集やオフライン編集では使いにくくなります。ユーザーが毎回ロックの解除を待たなければならないと、体験は重くなります。
CRDTでは、ユーザーがローカルで自由に編集し、その変更を後から他のレプリカと同期します。同時に行われた変更は、CRDTのルールに従って自動的にマージされます。ただし、これは「ユーザーの意図を完全に理解して自然な文章に直す」という意味ではありません。CRDTが保証するのは、定義された規則に従って全レプリカが収束することです。その結果が人間にとって自然かどうかは、データ型の設計やUIの見せ方にも依存します。
1.3 最終的に同じ状態へ収束する
CRDTの中心的な性質は、レプリカが一時的に異なる状態になっても、必要な更新をすべて受け取れば最終的に同じ状態へ収束することです。これにより、通信の順序が異なっても、ネットワーク遅延があっても、各レプリカは同じ結果へ近づきます。たとえば、ユーザーAの更新が先に届く端末もあれば、ユーザーBの更新が先に届く端末もありますが、すべての更新が届けば同じ状態になるように設計されます。
この性質は、最終的整合性や強い最終的整合性と関係します。CRDTでは、同期の途中では状態が違っていてもよいと考えます。その代わり、更新が伝播した後には同じ結果になることを重視します。これは、常に中央サーバーと通信して即時に整合性を取る方式とは異なり、可用性やローカル操作の速さを重視する設計に向いています。
2. なぜCRDTが必要なのか
CRDTが必要になる理由は、現代のアプリケーションでは、同じデータが複数の場所で同時に扱われることが増えているからです。ユーザーは複数端末で同じアカウントを使い、複数人で同じ文書を編集し、ネットワークが不安定な場所でも作業を続けます。このような環境では、サーバー中心の単純な同期方式だけでは、ユーザー体験や可用性に限界が出ることがあります。
従来の方式では、最後に保存した変更を採用するLast Write Wins、同時編集を禁止するロック、ユーザーに手動で競合を解決させる方法などが使われてきました。しかし、これらの方式は、変更の消失、待ち時間、操作の中断、ユーザー負担を生みやすいという課題があります。CRDTは、複数の変更をできるだけ自動的に統合し、ユーザーが作業を止めずに済む体験を作るために使われます。
2.1 オフラインでも作業できる
CRDTは、オフラインでも作業できるアプリケーションと相性が良い技術です。ユーザーがネットワークのない場所でメモを編集したり、飛行機の中でドキュメントを修正したり、現場で業務データを入力したりする場合、サーバーに接続できないからといって作業が止まるのは不便です。CRDTを使うと、ユーザーはローカルで編集を続け、再接続後に変更を同期できます。
このような体験は、単に便利というだけではありません。オフラインでも操作できることは、ユーザーにとって「自分のデータが手元にある」「アプリがネットワークに依存しすぎていない」という安心感にもつながります。Local-first Softwareの文脈では、ローカルで即時に操作できること、ネットワークが回復したら自然に同期されること、ユーザーがデータの所有感を持てることが重視されます。CRDTは、その実現に使われる代表的な技術の一つです。
2.2 共同編集を自然に扱える
CRDTは、共同編集を自然に扱うためにも使われます。複数人が同じ文書、コード、ホワイトボード、タスク、図形、データ構造を同時に編集する場合、毎回ロックをかけると作業が止まります。共同編集では、各ユーザーの操作がすぐに反映され、他のユーザーの変更も後から統合されることが重要です。
CRDTを使うと、ユーザーAが文書の先頭を編集し、ユーザーBが文書の末尾を編集し、ユーザーCが途中にコメントを追加するような状況でも、それぞれの変更を統合しやすくなります。もちろん、編集内容が意味的に衝突する場合は、人間の確認が必要になることもあります。しかし、少なくともデータ同期のレベルでは、変更が失われにくく、全員のレプリカが同じ状態へ収束しやすくなります。
2.3 中央サーバーに依存しすぎない
CRDTは、中央サーバーが常にすべての更新を即時に調整する必要がない設計と相性があります。もちろん、実務ではサーバーを使って同期、永続化、認証、権限管理を行うことが多いです。しかし、CRDTの考え方では、各レプリカがローカルで更新を受け入れ、後から他のレプリカと同期できます。そのため、中央サーバーが一時的に使えない場合でも、ローカル作業を続けられる設計が可能になります。
これは、P2Pアプリやローカルファーストな共同編集ツールにとって重要です。サーバーは同期を助ける中継点であって、常に唯一の正解を保持し続ける存在でなくてもよい場合があります。ただし、サーバーを使わない設計にすると、権限管理、暗号化、デバイス紛失時の対応、データ削除、監査などが難しくなるため、実際のプロダクトでは慎重な設計が必要です。
3. CRDTの基本概念
CRDTを理解するには、レプリカ、更新、マージ、収束、因果関係、最終的整合性といった概念を理解する必要があります。これらは分散システムでよく使われる言葉ですが、CRDTではデータ型の設計に直接関係します。特に、複数の更新が異なる順序で届いたとしても、最終的に同じ状態にできるかどうかが重要です。
CRDTは、単なる同期ライブラリではありません。内部には、更新同士が交換可能であること、マージが決定的であること、同じ情報を複数回受け取っても壊れないこと、一度反映された情報が正しく統合されることなど、数学的な性質が関係しています。開発者がすべての理論を完全に理解する必要はありませんが、基本概念を押さえておくと、どのデータにCRDTを使うべきか、どの場面で注意すべきかを判断しやすくなります。
| 概念 | 意味 |
|---|---|
| レプリカ | 同じデータのコピー |
| 更新 | レプリカに対する変更操作 |
| マージ | 複数の状態や更新を統合する処理 |
| 収束 | 最終的に同じ状態になること |
| 因果関係 | ある更新が別の更新より前に起きた関係 |
| 最終的整合性 | 時間が経つと各レプリカが一致する性質 |
| 強い最終的整合性 | 同じ更新集合を受け取れば同じ状態になる性質 |
3.1 レプリカ
レプリカとは、同じデータのコピーです。たとえば、共同編集文書が複数のユーザーのブラウザ、スマートフォン、デスクトップアプリ、サーバーに存在する場合、それぞれがレプリカです。CRDTでは、これらのレプリカが一時的に異なる内容を持つことを許容します。重要なのは、最終的に同じ更新を受け取ったとき、同じ状態へ収束できることです。
レプリカを前提にすると、アプリケーション設計も変わります。従来のように「サーバー上のデータだけが正しい」と考えるのではなく、「各端末が一時的に正当な状態を持ち、後から統合される」と考える必要があります。この考え方は、ユーザー体験を大きく変えます。ユーザーはサーバーの応答を待たずに編集でき、ネットワークが切れても作業を続けられます。
3.2 更新
更新とは、レプリカに対して行われる変更操作です。カウンターを増やす、集合に要素を追加する、Mapに値を設定する、テキストに文字を挿入する、リストから項目を削除するなどが更新に当たります。CRDTでは、更新がどのレプリカで発生し、どの順番で他のレプリカに届くかが重要になります。
単純な更新であればマージは比較的簡単です。たとえば、追加だけの集合なら、複数の集合を合体すればよい場合があります。しかし、削除や順序付きリスト、リッチテキスト、ネストしたJSON構造では、更新の意味が複雑になります。同時に同じ要素を削除した場合、同じ場所に文字を挿入した場合、片方が親要素を削除し、もう片方が子要素を編集した場合など、設計上の難しいケースが出てきます。
3.3 マージ
マージとは、複数のレプリカの状態や更新を統合する処理です。CRDTでは、このマージが決定的であることが重要です。つまり、同じ情報を持っているなら、どの順序でマージしても同じ結果になる必要があります。さらに、同じ更新を複数回受け取っても状態が壊れないようにする必要があります。
マージの設計は、CRDTの核心です。単純なカウンターや集合では比較的分かりやすいですが、テキストやリッチテキストでは非常に複雑になります。共同編集では、ユーザーの意図をなるべく壊さないように、挿入位置、削除対象、順序、履歴を扱う必要があります。CRDTライブラリを使う場合でも、アプリケーション側ではマージ後の状態をどうUIに表示するかを考える必要があります。
4. 最終的整合性とCRDT
CRDTは、最終的整合性と深く関係します。最終的整合性とは、複数のレプリカが一時的に異なる状態になっても、更新が伝播すれば最終的に同じ状態へ近づくという考え方です。強い整合性のように、すべての操作の直後に全レプリカが同じ状態であることを保証するのではなく、時間が経てば整合することを重視します。
この考え方は、ネットワーク障害やオフライン編集がある現実的な環境に向いています。ユーザーが操作するたびに中央サーバーへ確認し、全レプリカの状態を即時に一致させると、通信遅延や可用性の問題が発生します。CRDTは、一時的な不一致を許容する代わりに、ローカル操作の速さやシステムの可用性を高めやすくします。
4.1 一時的な不一致を許容する
CRDTでは、レプリカ間の一時的な不一致を自然なものとして扱います。たとえば、ユーザーAの端末では新しい行が追加されているが、ユーザーBの端末にはまだ届いていない、という状態があり得ます。この時点では二つのレプリカは異なる状態です。しかし、ネットワークが回復し、更新が同期されれば、両方の端末が同じ内容に近づきます。
この一時的な不一致を許容する設計は、ユーザー体験にとって大きな意味があります。ユーザーはネットワークを待たずに作業でき、アプリはローカルで即時に反応できます。その一方で、UI上では「同期中」「オフラインで保存済み」「他端末へ未反映」といった状態を分かりやすく見せることが重要です。技術的に収束できても、ユーザーが状態を理解できなければ不安につながります。
4.2 同じ更新集合なら同じ結果になる
CRDTの重要な性質は、各レプリカが同じ更新集合を受け取れば、更新の到着順序が違っても同じ結果になることです。たとえば、端末Aでは更新1、更新2の順に届き、端末Bでは更新2、更新1の順に届いたとしても、最終状態が同じになるように設計されます。
この性質を実現するためには、更新操作やマージ関数が特定の数学的性質を満たす必要があります。状態ベースCRDTでは、状態のマージが冪等、可換、結合的であることが重要になります。操作ベースCRDTでは、並行する操作が適切に交換可能であることや、必要な因果関係が守られることが重要になります。これらの性質によって、CRDTはネットワーク順序に依存しすぎずに収束できます。
4.3 強い整合性とは違う
CRDTは、強い整合性を提供する技術ではありません。強い整合性では、すべてのユーザーが常に同じ最新状態を見ることを目指します。一方、CRDTでは、一時的に異なる状態が存在することを許容します。この違いを理解せずにCRDTを使うと、適用すべきでない領域に使ってしまう危険があります。
たとえば、銀行残高、決済、在庫の最終確定、医療指示、法的承認などでは、最終的に収束すればよいというだけでは不十分です。特定の瞬間における正しい状態、操作順序、責任、監査ログが重要になります。このような領域では、CRDTを使うとしても、すべての状態に使うのではなく、共同編集コメントや下書きなど、強い整合性が必須ではない部分に限定するほうが安全です。
5. CRDTの種類
CRDTにはいくつかの種類があります。大きく分けると、状態をマージするState-based CRDT、操作を配信して適用するOperation-based CRDT、状態全体ではなく差分を送るDelta-state CRDT、テキストやリストの順序を扱うSequence CRDT、JSONや文書構造を扱うDocument CRDTなどがあります。それぞれ同期方法、データ量、配送条件、実装の難しさが異なります。
CRDTの種類を理解することは、どのライブラリを使うか、どのデータをCRDT化するかを考えるうえで重要です。たとえば、単純なカウンターと共同編集テキストでは、必要なCRDTの設計がまったく違います。状態全体を同期するのか、操作だけを同期するのか、削除や履歴をどう扱うのかによって、設計の複雑さが変わります。
5.1 State-based CRDT
State-based CRDTは、各レプリカが持つ状態そのものを他のレプリカへ送り、受け取った側が自分の状態とマージする方式です。日本語では状態ベースCRDTと呼べます。たとえば、追加だけの集合であれば、複数の集合をunionすることで統合できます。状態ベースCRDTでは、マージ関数が同じ情報を何度受け取っても壊れず、順序が違っても同じ結果になるように設計されます。
この方式の利点は、更新操作をすべて正確に配送しなくても、状態を受け取ってマージすれば収束しやすいことです。ネットワークが不安定でも、最終的に状態が届けば統合できます。一方で、状態全体を毎回送るとデータ量が大きくなる場合があります。大きな文書や長期間編集されたデータでは、状態全体の同期が重くなるため、差分同期や圧縮が必要になることがあります。
| 観点 | State-based CRDT |
|---|---|
| 同期対象 | レプリカの状態 |
| マージ方法 | 状態同士をmergeする |
| 強み | 配送順序に比較的強く、状態を受け取れば収束しやすい |
| 弱み | 状態全体が大きいと同期コストが高くなる |
| 向いている例 | カウンター、集合、比較的小さな状態共有 |
5.2 Operation-based CRDT
Operation-based CRDTは、状態そのものではなく、更新操作を他のレプリカへ送る方式です。日本語では操作ベースCRDTと呼べます。たとえば、「カウンターを1増やす」「集合に要素Aを追加する」「テキストの位置Xに文字を挿入する」といった操作を他のレプリカへ配信し、受け取ったレプリカが同じ操作を適用します。
この方式の利点は、状態全体を送らずに済むため、同期データを小さくしやすいことです。特に大きな文書や頻繁な編集では、操作だけを送るほうが効率的な場合があります。一方で、操作が確実に届くこと、重複して適用されないこと、必要な因果関係が守られることなど、配送条件が重要になります。操作が欠落したり、前提となる更新より先に届いたりすると、正しく適用できない場合があります。
| 観点 | Operation-based CRDT |
|---|---|
| 同期対象 | 更新操作 |
| マージ方法 | 各レプリカで操作を適用する |
| 強み | 状態全体を送らずに済み、効率的になりやすい |
| 弱み | 操作配送、重複排除、因果順序の扱いが難しい |
| 向いている例 | 共同編集、リアルタイム同期、頻繁な小さな更新 |
5.3 Delta-state CRDT
Delta-state CRDTは、State-based CRDTの考え方を保ちながら、状態全体ではなく、変更によって生じた差分だけを送る方式です。日本語では差分状態CRDTと表現できます。状態全体を送ると重くなる問題を避けるために、更新によって増えた情報や変化した部分だけを同期します。
Delta-state CRDTは、状態ベースCRDTの収束しやすさと、操作ベースCRDTの効率性の中間にあるような考え方です。状態全体を送るより軽く、操作だけを送るよりマージの扱いを単純にできる場合があります。ただし、差分をどの範囲で作るか、どの差分がどのレプリカに届いたか、欠落時にどう回復するかを設計する必要があります。
| 観点 | Delta-state CRDT |
|---|---|
| 同期対象 | 状態の差分 |
| マージ方法 | 差分を既存状態へmergeする |
| 強み | 状態全体より軽く、収束性も保ちやすい |
| 弱み | 差分管理や欠落回復の設計が必要 |
| 向いている例 | 大きめの状態を効率よく同期したいケース |
5.4 Sequence CRDT
Sequence CRDTは、テキストやリストのような順序付きデータを扱うCRDTです。共同編集エディタでは、文字をどこに挿入するか、同時に同じ位置へ挿入された文字の順序をどう決めるか、削除された文字をどう扱うかが重要になります。単純な集合と違い、順序が意味を持つため、設計が非常に難しくなります。
Sequence CRDTでは、各要素に一意な識別子を付け、隣接関係や順序関係を保ちながらマージする方法が使われます。ユーザーAが文の途中に文字を挿入し、ユーザーBも同じ場所に別の文字を挿入した場合、それらをどの順番で並べるかを決定する必要があります。さらに、削除済みの文字や位置情報をどれくらい保持するかも問題になります。テキストエディタやリッチテキストエディタでは、Sequence CRDTの設計が共同編集体験の品質を大きく左右します。
| 観点 | Sequence CRDT |
|---|---|
| 同期対象 | テキスト、リスト、順序付き要素 |
| マージ方法 | 要素IDや順序情報を使って統合する |
| 強み | 共同テキスト編集やリスト編集に向いている |
| 弱み | メタデータ、削除、意図保存が難しい |
| 向いている例 | collaborative editor, code editor, rich text editor |
5.5 JSON / Document CRDT
JSON / Document CRDTは、Map、List、Text、Registerなどを組み合わせた構造化データを扱うCRDTです。実際のアプリケーションでは、単純なカウンターや集合だけでなく、ネストしたオブジェクト、配列、テキスト、属性、設定値などをまとめて扱う必要があります。AutomergeやYjsのようなライブラリは、このような構造化ドキュメントを共同編集できる形で扱うことを目指しています。
この種類のCRDTでは、キーと値の更新、リストの順序、テキストの挿入削除、オブジェクトの削除、同時に異なる属性が編集された場合の統合など、多くのケースを扱う必要があります。実務でCRDTを使う場合、開発者は低レベルなCRDTデータ型を直接設計するより、YjsやAutomergeのようなライブラリが提供する共有データ型を使うことが多くなります。
| 観点 | JSON / Document CRDT |
|---|---|
| 同期対象 | JSON風の構造化データ、文書、Map、List、Text |
| マージ方法 | 複数データ型のCRDTルールを組み合わせる |
| 強み | 実際のアプリデータに近い形で扱いやすい |
| 弱み | ネスト、削除、履歴、サイズ管理が複雑 |
| 向いている例 | メモアプリ、ドキュメント、設定同期、共同作業ツール |
6. 代表的なCRDTデータ型
CRDTには、カウンター、集合、レジスター、マップ、リスト、テキストなど、さまざまなデータ型があります。扱うデータの性質によって、適したCRDTは変わります。単純なカウンターや追加だけの集合は比較的理解しやすいですが、削除を含む集合、順序付きリスト、共同編集テキスト、リッチテキストは複雑になります。
代表的なCRDTデータ型を理解すると、CRDTがどのように競合を解決するのかが分かりやすくなります。たとえば、カウンターでは各レプリカの増加分を合計すればよい場合があります。集合では、追加だけならunionで統合できます。しかし、削除を含めると、削除対象をどの追加操作に対応させるかを考える必要があります。
| データ型 | 用途 |
|---|---|
| G-Counter | 増加だけのカウンター |
| PN-Counter | 増減できるカウンター |
| G-Set | 追加だけの集合 |
| 2P-Set | 追加と削除を扱う集合 |
| OR-Set | 追加・削除をより柔軟に扱う集合 |
| LWW-Register | 最後の書き込みを採用する値 |
| Map CRDT | キーと値の構造を扱う |
| Sequence CRDT | テキストやリストを扱う |
6.1 Counter CRDT
Counter CRDTは、数値を扱うCRDTです。代表例として、G-CounterとPN-Counterがあります。G-Counterは増加だけを扱うカウンターで、PN-Counterは増加と減少の両方を扱えるカウンターです。複数のレプリカがそれぞれカウントを増やし、後から合計するような用途に向いています。
カウンターはCRDTの入門例として理解しやすいデータ型です。各レプリカが自分の増加分を保持し、マージ時には各レプリカごとの最大値を採用して合計します。これにより、同じ更新が複数回届いても二重にカウントされにくく、順序が違っても最終値が一致しやすくなります。ただし、減算を扱う場合は、増加用と減少用のカウンターを分けて管理するなどの工夫が必要です。
6.2 Set CRDT
Set CRDTは、集合を扱うCRDTです。G-Setは追加だけを許可する集合で、複数のレプリカで追加された要素をunionすることで統合できます。追加だけなら非常にシンプルです。しかし、実際のアプリケーションでは削除も必要になることが多く、そこから設計が複雑になります。
削除を扱うSet CRDTでは、同時に追加と削除が起きたときにどちらを優先するかを考える必要があります。2P-Setでは、追加集合と削除集合を分けて持ち、一度削除された要素は再追加できないという制約があります。OR-Setでは、追加ごとに識別子を付け、削除対象をより正確に扱うことで、より柔軟な集合操作を実現します。集合は単純に見えますが、削除と同時更新を含めるとCRDTらしい難しさが出てきます。
6.3 Register CRDT
Register CRDTは、単一の値を扱うCRDTです。代表例としてLWW-Registerがあります。LWWはLast Writer Winsの略で、複数の値が競合した場合に、タイムスタンプや順序情報を使って最後の書き込みを採用する方式です。設定値や単一フィールドの同期で使われることがあります。
ただし、LWWは分かりやすい反面、同時更新の片方を失う可能性があります。たとえば、ユーザーAがタイトルを変更し、同時にユーザーBも別のタイトルへ変更した場合、片方の変更だけが残ることになります。これは収束という意味では問題ありませんが、ユーザーの意図を保存するという意味では弱い場合があります。そのため、重要な編集内容をLWWだけで扱う場合は慎重に判断する必要があります。
6.4 Text CRDT
Text CRDTは、共同編集テキストを扱うためのCRDTです。文字列は単なる値ではなく、順序を持った要素の列です。ユーザーが同じ場所に同時に文字を挿入したり、片方が削除した場所にもう片方が挿入したりするため、単純なマージでは対応できません。
Text CRDTでは、各文字や挿入位置に一意な識別子を付け、並行編集が起きても順序を決定できるようにします。これにより、複数人が同じ文書を編集しても、各レプリカが最終的に同じ文字列へ収束できます。ただし、メタデータが増えやすいこと、削除済み文字の情報をどれくらい保持するか、リッチテキストの装飾や段落構造をどう扱うかなど、実装上の課題は多くあります。
7. CRDTとOTの違い
CRDTと比較されることが多い技術に、OTがあります。OTはOperational Transformationの略で、日本語では操作変換と訳されます。OTは、同時に発生した操作を変換し、各ユーザーの編集結果が整合するようにする技術です。Google Docsのようなリアルタイム共同編集の文脈でよく知られています。
CRDTとOTはどちらも共同編集に使われますが、考え方が異なります。OTは操作を受け取ったときに、その操作を他の並行操作に合わせて変換します。一方、CRDTは、データ型や操作そのものが最終的に収束するように設計されています。どちらが常に優れているというより、ネットワーク構成、オフライン要件、中央サーバーの有無、扱うデータ型、実装コストによって向き不向きがあります。
| 比較項目 | CRDT | OT |
|---|---|---|
| 基本思想 | 収束するデータ型を設計する | 操作を変換して整合性を保つ |
| 中央サーバー | 必須ではない設計が可能 | 中央サーバー前提の実装が多い |
| オフライン対応 | 相性が良い | 設計次第で難しくなる |
| 実装難易度 | データ型ごとの設計が難しい | 変換ルールが複雑になりやすい |
| 主な用途 | Local-first、P2P、共同編集 | 共同編集、文書編集 |
| 代表的課題 | メタデータ肥大、意図保存、削除処理 | 変換アルゴリズムの正しさ |
7.1 OTは操作を変換する
OTでは、複数のユーザーが同時に編集した場合、それぞれの操作を他の操作に合わせて変換します。たとえば、ユーザーAが3文字目に文字を挿入し、同時にユーザーBが2文字目に文字を挿入した場合、ユーザーAの操作をそのまま適用すると位置がずれる可能性があります。そのため、受信側では操作の位置を変換して、意図に近い結果になるように調整します。
OTは強力ですが、正しい変換ルールを設計するのは難しい場合があります。特に、複数の操作が複雑に絡む場合、中央サーバーで順序を管理しない場合、オフラインで長時間編集された変更を後から統合する場合には、アルゴリズムの複雑さが増します。OTは実績のある技術ですが、実装には慎重な設計が必要です。
7.2 CRDTは収束をデータ型に組み込む
CRDTでは、操作を後から変換するというより、データ型や操作の設計によって、どの順序で更新が届いても最終的に同じ状態へ収束できるようにします。各要素に一意なIDを付けたり、更新履歴や因果情報を持ったりすることで、同時編集を自動的に統合します。
この考え方は、オフラインファーストやP2Pと相性が良いです。中央サーバーが常に正しい順序を決めなくても、各レプリカが更新を受け取り、最終的に同じ状態へ収束できます。ただし、CRDTも簡単ではありません。特にリッチテキストや複雑な構造化データでは、メタデータ肥大、削除処理、ユーザー意図の保存が課題になります。
7.3 どちらが優れているかではない
CRDTとOTは、どちらが絶対に優れているという話ではありません。中央サーバーを前提にしたリアルタイム文書編集では、OTが適している場合もあります。一方で、オフラインファースト、ローカルファースト、P2P、複数端末同期を重視するならCRDTが有力になります。
重要なのは、プロダクトの要件に合わせて選ぶことです。リアルタイム性、オフライン対応、同期方式、データ型、既存ライブラリ、デバッグしやすさ、チームの知識を考える必要があります。技術選定では、理論的な美しさだけでなく、実際に運用できるかどうかが重要です。
8. CRDTの用途
CRDTは、複数のレプリカが同じデータを編集し、後から同期する必要がある場面で使われます。代表的な用途は、共同編集エディタ、オフラインファーストアプリ、分散データベース、P2Pアプリ、共同ホワイトボード、リアルタイムコラボレーションツールなどです。特に、ユーザーがネットワーク状態に関係なく作業を続けられることが重要なプロダクトでは、CRDTの価値が高くなります。
ただし、CRDTはすべての同期に必要なわけではありません。単純なサーバー中心のCRUDアプリで、同時編集やオフライン編集がほとんどない場合は、通常のAPIとデータベーストランザクションで十分です。CRDTは、データ同期の難しい問題を解決するための強力な技術ですが、そのぶん設計・実装・運用の複雑さも増えます。
8.1 共同編集エディタ
CRDTの代表的な用途は共同編集エディタです。テキストエディタ、リッチテキストエディタ、コードエディタ、ホワイトボード、ノートアプリなどで、複数人が同時に同じデータを編集する場合に使われます。ユーザーが入力した内容をローカルに即時反映し、他ユーザーの変更を後から統合することで、スムーズな共同編集体験を作れます。
共同編集では、同期だけでなくUIも重要です。他ユーザーのカーソル、選択範囲、編集中状態、コメント、変更履歴、同期状態を適切に表示する必要があります。CRDTはデータを収束させる基盤ですが、ユーザーが安心して共同作業できる体験を作るには、presenceやundo/redo、競合時の説明も含めた設計が必要です。
8.2 オフラインファーストアプリ
オフラインファーストアプリでは、ユーザーがネットワークに接続できない状態でも作業を続けられることを重視します。CRDTを使うと、ローカルで変更を保存し、ネットワークが回復したタイミングで他の端末やサーバーと同期できます。これは、モバイルアプリ、現場作業アプリ、メモアプリ、タスク管理アプリなどで有効です。
ただし、オフライン中に複数の端末で同じデータが編集される場合、再接続時にどのように統合されるかをユーザーに分かりやすく見せる必要があります。技術的には自動マージできても、ユーザーが「何が起きたのか分からない」と感じると、信頼性が下がります。そのため、同期状態、ローカル保存、未同期変更、他端末からの変更を適切に表示することが重要です。
8.3 Local-first Software
Local-first Softwareは、データをまずユーザーの端末に置き、クラウドは同期や共有のために使うという考え方です。従来のクラウド中心アプリでは、サーバーが正本であり、端末はサーバーの表示窓のように扱われることがあります。一方、Local-firstでは、ローカルにデータを持ち、オフラインでも高速に操作でき、必要に応じて他の端末やユーザーと同期します。
CRDTは、このLocal-firstの考え方と非常に相性が良い技術です。ローカルで即時に変更を反映し、後から他のレプリカとマージできるため、クラウドの便利さとローカルの速さを両立しやすくなります。ただし、Local-firstを本格的に実現するには、同期だけでなく、データ所有、暗号化、権限、バックアップ、デバイス紛失時の対応なども設計する必要があります。
9. Automerge
Automergeは、CRDTを使ったlocal-first sync engineとして知られるライブラリです。複数ユーザーや複数デバイスで一つのドキュメントを扱い、ローカルで変更を保存し、後から同期できることを重視しています。共同編集、オフライン編集、変更履歴、ローカルファーストなアプリケーションを作る際に候補になります。
Automergeの特徴は、開発者が低レベルなCRDTアルゴリズムを直接実装しなくても、構造化されたデータをローカルで編集し、他のレプリカとマージできる点です。文書、メモ、構造化データ、共同作業ツールなどで、ローカル編集と同期を両立したい場合に使いやすい選択肢です。ただし、実際のプロダクトでは、データサイズ、同期経路、権限、永続化、履歴管理を慎重に設計する必要があります。
9.1 ローカルファースト向け
Automergeは、ローカルファーストなアプリケーションに向いています。ユーザーはローカルで即時に変更でき、サーバーの応答を待つ必要がありません。ネットワークがない状態でも操作を続けられ、再接続後に変更を同期できます。これは、メモアプリ、共同ドキュメント、構造化データ編集ツールなどで非常に有用です。
ただし、ローカルファーストな体験を作るには、Automergeを導入するだけでは不十分です。ユーザーに同期状態をどう見せるか、ローカルデータをどこに保存するか、複数デバイスの認証をどうするか、データが大きくなったときにどう圧縮・整理するかを設計する必要があります。同期エンジンは基盤であり、良い体験には周辺設計が欠かせません。
9.2 変更履歴を持つ
Automergeは、変更履歴を扱う設計と相性があります。CRDTでは、単に現在の状態だけでなく、どのような変更が行われたかを保持することで、同期やマージを実現します。このため、ブランチ、マージ、変更履歴の追跡、過去状態の復元といった考え方とも結びつきやすくなります。
一方で、履歴を持つということは、データサイズが増えやすいということでもあります。長期間編集される文書や、多数のユーザーが頻繁に更新するデータでは、履歴やメタデータが膨らむ可能性があります。実運用では、圧縮、スナップショット、不要履歴の整理、同期コストの監視が重要になります。
9.3 向いているケース
Automergeは、ローカル編集、オフライン利用、複数端末同期、共同編集、データ所有を重視するアプリに向いています。たとえば、メモアプリ、ドキュメントアプリ、構造化ノート、共同作業ツール、オフライン対応の業務アプリなどです。ユーザーがネットワークに依存せず作業し、後から自然に同期される体験を作りたい場合に候補になります。
ただし、厳密なトランザクション、即時の強い整合性、法的に厳密な承認状態、金融的な正確性が必要なシステムには、そのまま適用しにくい場合があります。Automergeは共同編集やローカルファーストには強いですが、すべてのデータ整合性問題を置き換えるものではありません。
10. Yjs
Yjsは、共同編集アプリケーション向けのCRDT実装として広く使われているライブラリです。テキスト、Map、Arrayなどの共有データ型を提供し、それらを複数ユーザーで同時に編集し、自動的に同期・マージできるようにします。リアルタイム共同編集、オフラインサポート、エディタ連携、ネットワーク非依存の同期設計が特徴です。
Yjsは、ProseMirror、Tiptap、Monaco、Quill、CodeMirrorなど、複数のエディタとの連携で知られています。これにより、リッチテキストエディタやコードエディタに共同編集機能を追加しやすくなります。ただし、Yjsを導入しても、カーソル表示、presence、権限、永続化、同期サーバー、undo/redo、アクセス制御は別途設計する必要があります。
10.1 Shared Types
Yjsの中心にはShared Typesがあります。Shared Typesは、Map、Array、Textのようなデータ型を複数レプリカで共有できる仕組みです。開発者は通常のデータ構造に近い感覚で操作しながら、内部ではCRDTとして変更が管理されます。これにより、低レベルなマージアルゴリズムを自分で実装せずに、共同編集データを扱いやすくなります。
Shared Typesを使うと、複数ユーザーが同じデータに対して同時に変更を行っても、Yjsがそれらを統合します。たとえば、共同テキスト編集では、複数人が同じ文書に文字を挿入・削除しても、各クライアントが最終的に同じ文書状態へ収束します。これは、共同編集UIを作るうえで非常に重要な基盤です。
10.2 エディタ連携
Yjsは、エディタ連携が強い点でも使われます。ProseMirrorやTiptapのようなリッチテキストエディタ、MonacoやCodeMirrorのようなコードエディタと組み合わせることで、共同編集機能を比較的導入しやすくなります。エディタ側が持つ文書モデルや選択範囲、カーソル管理と、Yjsの共有データ型を接続することで、複数人編集を実現します。
ただし、エディタ連携では、単にテキストを同期するだけでは不十分です。他ユーザーのカーソル表示、選択範囲、編集中の状態、接続状態、ユーザー名、色分け、undo/redoの範囲などを設計する必要があります。Yjsは同期の基盤を提供しますが、共同編集として自然な体験にするには、UI/UXの設計が重要です。
10.3 ネットワーク非依存
Yjsは、特定のネットワーク方式に強く依存しない設計として使えます。WebSocket、WebRTC、IndexedDB、サーバー同期など、アプリの要件に応じてさまざまな同期・永続化構成を組み合わせることができます。これにより、リアルタイム共同編集だけでなく、オフライン編集、ローカル保存、P2P同期などにも応用しやすくなります。
一方で、自由度が高いということは、設計責任も大きいということです。どこに永続化するのか、どの通信方式を使うのか、サーバーは何を保持するのか、権限管理をどこで行うのか、同期失敗時にどう回復するのかを決める必要があります。Yjsは強力な基盤ですが、プロダクト全体の同期設計は開発側が行う必要があります。
11. CRDTのメリット
CRDTのメリットは、同時編集、オフライン編集、分散同期に強いことです。複数のユーザーや端末が同じデータを独立して編集しても、後から変更を統合し、最終的に同じ状態へ収束できます。これにより、ネットワークが不安定な環境でもユーザーが作業を止めずに済み、アプリはローカルで素早く反応できます。
また、CRDTは、ユーザーに手動で競合解決を求める場面を減らせる可能性があります。従来の同期では、「どちらの変更を残しますか」とユーザーに選ばせることがありますが、これは負担が大きく、文脈によっては判断も難しくなります。CRDTでは、データ型に組み込まれた規則によって自動的にマージされるため、共同編集やマルチデバイス同期を自然に見せやすくなります。
11.1 ローカルで即時に反映できる
CRDTを使うと、ユーザーの操作をローカルで即時に反映しやすくなります。サーバーに送信して応答を待ってから画面を更新するのではなく、まずローカル状態を更新し、その変更を後から同期できます。このため、入力、ドラッグ、テキスト編集、図形編集、リスト操作などが速く感じられます。特に共同編集エディタやホワイトボードのように、操作の即時性が体験品質に直結するアプリでは重要です。
この即時反映は、ユーザーの心理にも影響します。ユーザーは自分の操作がすぐに画面に現れることで、アプリが軽く、信頼できると感じます。ネットワーク遅延によって入力が止まったり、保存待ちで操作できなくなったりすると、体験は大きく悪化します。CRDTは、ローカル操作を優先しながら同期を後回しにできるため、滑らかな体験を作る助けになります。
11.2 競合解決を自動化しやすい
CRDTでは、データ型にマージ規則が組み込まれているため、競合解決を自動化しやすくなります。たとえば、複数人が同じ文書に別々の文字を挿入した場合、両方の変更を統合し、各レプリカが同じ文書状態へ収束できます。これにより、変更が上書きで失われるリスクを減らせます。
ただし、自動化できるのは技術的なマージであって、人間の意図を完全に判断することではありません。たとえば、二人が同じ文の意味を別々に書き換えた場合、CRDTは両方の変更を統合できても、文章として自然かどうかまでは保証できません。そのため、CRDTを使う場合でも、変更履歴、コメント、レビュー、他ユーザーのpresenceなどを組み合わせ、ユーザーが必要に応じて修正できるUIを用意することが重要です。
11.3 可用性が高い
CRDTは、各レプリカが独立して更新できるため、可用性を高めやすい技術です。中央サーバーに一時的に接続できなくても、ユーザーはローカルで作業を続けることができます。これは、モバイル環境、ネットワークが不安定な地域、現場作業、分散チーム、P2Pアプリなどで特に価値があります。
可用性が高いということは、単にシステムが落ちにくいというだけではありません。ユーザーが自分の作業を中断せずに済むこと、ネットワーク状態によって体験が大きく劣化しないこと、ローカルデータがあるためアプリが即時に起動・表示できることも含まれます。CRDTは、クラウド同期の便利さとローカル操作の安定性を両立させるための重要な選択肢になります。
12. CRDTのデメリットと注意点
CRDTには強力なメリットがありますが、導入すればすべてが簡単になるわけではありません。むしろ、通常のサーバー中心のCRUDアプリよりも、同期、データ構造、削除、履歴、権限、デバッグ、運用が複雑になることがあります。CRDTは、同時編集やオフライン編集が本当に必要な場合には大きな価値を持ちますが、要件が弱い場合には過剰設計になりやすい技術です。
また、CRDTは「競合をなくす技術」ではなく、「競合しても収束できるようにする技術」です。自動マージされた結果が、常にユーザーの期待通りになるとは限りません。アプリケーション側では、同期状態、変更履歴、マージ結果、他ユーザーの編集状態を分かりやすく見せる必要があります。CRDTはデータ同期の基盤であり、良い共同編集体験を作るにはUI/UX設計も欠かせません。
12.1 データサイズが増えやすい
CRDTでは、単純な現在値だけでなく、更新履歴、識別子、因果関係、削除済み要素の情報などを保持することがあります。そのため、通常のデータ構造よりもデータサイズが大きくなる場合があります。特に、長期間編集される文書、多数のユーザーが編集するデータ、大量のテキスト挿入・削除が発生するエディタでは、メタデータが膨らみやすくなります。
データサイズが増えると、同期時間、メモリ使用量、保存容量、読み込み速度に影響します。ライブラリ側で圧縮や最適化が行われていても、実際のプロダクトでは計測が必要です。長期運用では、スナップショット、履歴圧縮、ガベージコレクション、不要データの整理、同期範囲の分割などを考える必要があります。
12.2 削除処理が難しい
CRDTでは、削除処理が特に難しい領域です。なぜなら、あるレプリカで削除された要素が、別のレプリカではまだ追加操作として届いていない可能性があるからです。削除済みであることを完全に忘れてしまうと、後から届いた古い追加操作によって要素が復活してしまう場合があります。そのため、削除済み情報をtombstoneとして保持する設計が使われることがあります。
しかし、tombstoneを長期間保持するとデータサイズが増えます。一方で、早く消しすぎると正しいマージができなくなる可能性があります。つまり、削除済み情報をいつまで保持するか、どのタイミングで安全にガベージコレクションできるかが重要になります。この問題は、単純なデモでは見えにくいですが、長期間運用する共同編集アプリでは大きな課題になります。
12.3 意図通りのマージは難しい
CRDTは、数学的・技術的に収束することを重視します。しかし、ユーザーの意図を完全に理解するわけではありません。たとえば、二人が同じ文章を同時に編集した場合、CRDTは両方の文字を残すことができますが、最終的な文章が自然になるとは限りません。構造化データでも、片方が親オブジェクトを削除し、もう片方がその子要素を編集した場合、アプリとしてどの結果が自然かを設計する必要があります。
そのため、CRDTを使う場合でも、UIでユーザーの理解を助ける必要があります。他ユーザーのカーソルやpresenceを表示する、変更履歴を見せる、レビューできるようにする、undo/redoを適切に設計する、同期状態を明示するなどの工夫が重要です。CRDTは競合解決を自動化しやすくしますが、共同作業の意味的な調整まで完全に代替するわけではありません。
13. CRDTが向いているケース
CRDTは、複数のユーザーや複数の端末が同じデータを編集し、その変更を後から自然に統合したい場合に向いています。特に、同時編集、オフライン編集、複数端末同期、P2P同期、Local-first Softwareのように、中央サーバーへ常時接続できない、または中央サーバーの応答を待ちたくない体験では、CRDTの価値が高くなります。
CRDTが向いているかどうかは、単に「共同編集機能があるか」だけでは決まりません。ユーザーがネットワークを待たずに操作したいのか、複数人が同じ文書やボードを編集するのか、変更が一時的に異なる状態になっても最終的に収束すればよいのか、競合時に自動マージされることがユーザーにとって自然なのかを考える必要があります。CRDTは、複雑な同期問題が本当に存在するプロダクトで力を発揮します。
13.1 共同編集ツール
CRDTは、共同編集ツールに非常に向いています。複数人が同じ文書、コード、図形、ホワイトボード、リスト、ノートを同時に編集する場合、変更を失わずに統合する仕組みが必要です。ユーザーが入力した内容をすぐに自分の画面へ反映し、他のユーザーの変更も遅れて統合することで、リアルタイム共同編集の体験を作れます。
共同編集ツールでは、データが正しく収束するだけでは十分ではありません。誰がどこを編集しているのか、どの変更が自分の変更なのか、同期済みか未同期か、他ユーザーが入力中かを分かりやすく見せる必要があります。CRDTは裏側の同期を支えますが、表側の共同作業体験はプロダクト設計で作る必要があります。特に、リッチテキストやホワイトボードのように編集対象が複雑な場合は、presence、カーソル表示、選択範囲、変更履歴の設計が重要になります。
13.2 オフラインファーストアプリ
CRDTは、オフラインファーストアプリに向いています。ネットワークがない状態でもユーザーが作業でき、後から自然に同期できるためです。メモ、タスク管理、現場入力、フィールドワーク、教育アプリ、モバイル業務アプリなど、通信環境が不安定でも作業を止めたくないアプリで価値があります。
ただし、オフライン中に複数端末で同じデータが編集されると、再接続時にマージが発生します。その結果がユーザーにとって自然かどうかを考える必要があります。技術的にマージできても、ユーザーが「なぜこの内容になったのか」を理解できないと不信感につながります。オフラインファーストでは、ローカル保存済み、同期中、同期済み、他端末からの更新あり、といった状態を丁寧に見せることが大切です。
13.3 Local-first Software
Local-first Softwareでは、データがまずローカルにあり、クラウドは同期や共有のために使われます。CRDTは、この考え方と相性が良いです。ローカルで即時に編集でき、ネットワークが回復したら他のレプリカと同期できるため、クラウドアプリの便利さとローカルアプリの速さを両立しやすくなります。
Local-firstでは、ユーザーが自分のデータにアクセスできること、オフラインでも使えること、複数端末で同期できること、共同編集できることが重視されます。ただし、データが複数の場所に存在するため、暗号化、アクセス制御、デバイス管理、削除要求、バックアップなどの設計も必要になります。CRDTは中核技術の一つですが、Local-first全体を実現するには、同期だけでなく、セキュリティ、運用、データ所有の設計も重要です。
13.4 複数端末同期
CRDTは、同じユーザーが複数端末で同じデータを編集するケースにも向いています。たとえば、スマートフォンでメモを追加し、後からPCで内容を修正し、さらにタブレットでチェックリストを更新するような場面です。通常の同期方式では、どの端末の変更が最新か、どちらを上書きするかが問題になりますが、CRDTを使うと、各端末の変更を後から統合しやすくなります。
複数端末同期では、ユーザー本人の操作であっても、同時編集に近い状況が発生します。スマートフォンがオフラインで編集され、PCもオンラインで編集されていた場合、再接続後に両方の変更を統合する必要があります。CRDTは、このようなマルチデバイス時代の同期設計に適した選択肢になります。
14. CRDTが向いていないケース
CRDTは強力ですが、すべてのシステムに向いているわけではありません。特に、強い整合性が必要な処理、厳密なトランザクションが必要な処理、法的・金融的に正確な順序や状態が必要な処理では、CRDTだけに頼るのは危険です。CRDTは一時的な不一致を許容するため、「常に全員が同じ最新状態を見なければならない」用途には不向きな場合があります。
たとえば、銀行残高、在庫の最終確定、チケット販売、医療指示、法的承認状態などでは、最終的に収束すればよいというだけでは不十分です。どの瞬間に誰が何を行ったか、どの状態が正式か、どの操作が無効かを厳密に管理する必要があります。このような領域では、トランザクション、ロック、中央サーバーでの検証、監査ログが重要になります。
14.1 強い整合性が必要な処理
CRDTは一時的な不一致を許容します。そのため、強い整合性が必要な処理には注意が必要です。たとえば、在庫が残り1個の商品を二人が同時に購入した場合、最終的に収束するだけでは遅いかもしれません。この場合、二重販売を防ぐために、購入処理の瞬間に厳密な整合性を保つ必要があります。
このような処理では、サーバー側のトランザクション、ロック、排他制御、整合性制約が重要になります。CRDTは、下書き、コメント、共同編集、ローカル状態の同期には向いていても、最終的な確定処理には向かない場合があります。アプリ内でも、CRDTで扱うデータと強い整合性で扱うデータを分ける設計が現実的です。
14.2 ビジネスルールが複雑な処理
ビジネスルールが複雑な処理では、CRDTの自動マージだけでは不十分な場合があります。承認フロー、権限、監査、締め処理、法的責任が関係する場合、単にデータが収束するだけではなく、誰がいつどの権限で操作したか、どの順序で承認されたか、どの状態が正式なものかを管理する必要があります。
このような領域では、CRDTを導入するにしても、非正式な編集や下書きに限定するほうが安全です。正式な承認や確定状態は、サーバー側で厳密に管理する必要があります。CRDTはデータ同期の技術であり、ビジネスルールや法的判断を自動的に解決するものではありません。
14.3 単純なアプリでは過剰になる
単純なCRUDアプリでは、CRDTは過剰設計になることがあります。ユーザーが基本的にオンラインで、一人が一つのデータを編集し、同時編集もオフライン編集もほとんど必要ないなら、通常のAPIとデータベースで十分です。CRDTを導入すると、同期、永続化、権限、デバッグ、データサイズの管理が複雑になります。
技術的に面白いから導入するのではなく、要件が本当にCRDTを必要としているかを確認することが重要です。CRDTは強力ですが、その価値は複雑な同期問題がある場合に発揮されます。単純なアプリに入れると、メリットよりも保守コストのほうが大きくなる可能性があります。
14.4 自動マージが業務上危険なケース
CRDTは自動的にマージできることが強みですが、業務によっては自動マージが危険になる場合があります。たとえば、契約文書、法務レビュー、医療記録、会計処理、承認済みの業務データなどでは、複数人の変更を自動的に統合するより、人間が差分を確認し、正式に採用する変更を決める必要があります。
このようなケースでは、CRDTを完全に避ける必要はありませんが、使う範囲を慎重に分けるべきです。下書きやコメント、共同メモにはCRDTを使い、最終確定や承認済み状態にはトランザクションやレビュー工程を使う設計が現実的です。CRDTの自動マージは便利ですが、すべての業務判断を自動化してよいわけではありません。
15. CRDTを設計するときのポイント
CRDTを設計するときは、まず何を同期するのかを明確にする必要があります。テキストなのか、JSONなのか、リストなのか、集合なのか、カウンターなのかによって、適したCRDTは異なります。また、同時更新時にどのような結果が自然かを考えることも重要です。技術的に収束するだけではなく、ユーザーにとって納得できる結果になるかを確認する必要があります。
次に、同期経路、永続化、権限、暗号化、データサイズ、ガベージコレクション、undo/redo、UI表示を設計します。CRDTはデータ構造の問題に見えますが、実際にはプロダクト全体の設計に関係します。特に、共同編集やオフラインファーストでは、同期状態や他ユーザーの存在をどう見せるかが体験品質に直結します。
15.1 データ型を正しく選ぶ
CRDTでは、データ型の選択が非常に重要です。カウンターにはカウンター向けCRDT、集合には集合向けCRDT、テキストにはSequence CRDT、構造化データにはDocument CRDTのように、用途に合ったデータ型を選ぶ必要があります。不適切なデータ型を選ぶと、マージ結果が不自然になったり、データサイズが増えたり、後から仕様変更が難しくなったりします。
たとえば、ユーザーが同時に編集する文章を単純なLWW-Registerで扱うと、片方の変更が失われる可能性があります。一方、テキスト用CRDTを使えば、複数の挿入や削除をより適切に統合できます。CRDT導入前には、同期対象のデータがどのような構造を持ち、どのような同時更新が起きるのかを整理することが大切です。
15.2 同期範囲を決める
CRDTを使うときは、アプリ内のすべてのデータをCRDT化する必要はありません。むしろ、すべてをCRDTで扱おうとすると、データ構造、権限管理、同期、デバッグが複雑になりすぎることがあります。CRDTに向いているのは、同時編集やオフライン編集が必要なデータです。たとえば、共同文書の本文、ホワイトボード上のオブジェクト、共有メモ、コメントの下書きなどです。
一方で、決済状態、正式な承認状態、監査対象のログ、在庫の確定数などは、CRDTではなくサーバー側の厳密な管理に任せるほうが安全な場合があります。CRDTを設計するときは、どのデータをCRDTで扱い、どのデータを通常のデータベースやトランザクションで扱うのかを明確に分ける必要があります。
15.3 UIで状態を説明する
CRDTが自動的にマージしても、ユーザーが状況を理解できなければ良い体験にはなりません。共同編集では、他ユーザーのカーソル、選択範囲、編集中状態、接続状態、同期状態を表示することが重要です。オフライン編集では、現在の変更がローカル保存済みなのか、サーバー同期済みなのか、他端末に反映済みなのかを伝える必要があります。
特に、マージ結果がユーザーの予想と違う場合、説明なしでは不信感につながります。変更履歴、差分表示、復元機能、コメント、通知などを組み合わせることで、ユーザーは何が起きたのかを理解しやすくなります。CRDTは裏側の同期技術ですが、表側の体験設計がなければ、ユーザーにとっては不透明な自動変更に見えてしまう可能性があります。
15.4 セキュリティと権限を設計する
CRDTは同期とマージの技術であり、権限管理を自動的に解決するものではありません。誰がどのデータを編集できるのか、誰が削除できるのか、誰が共有できるのか、どの端末が同期に参加できるのかを別途設計する必要があります。特にP2Pやローカルファースト構成では、データが複数の場所に存在するため、セキュリティ設計が重要になります。
暗号化、認証、アクセス制御、端末紛失時の対応、データ削除要求、監査ログなども考える必要があります。CRDTを使うと、データがローカルにも存在し、複数のレプリカに広がるため、従来のサーバー中心の権限設計だけでは不十分な場合があります。同期できることと、同期してよいことは別問題です。
15.5 データサイズと履歴管理を考える
CRDTでは、変更履歴、識別子、削除済み情報、因果関係などのメタデータが増えることがあります。短いデモでは問題にならなくても、実際のプロダクトで長期間使うと、データサイズ、読み込み速度、同期時間、メモリ使用量に影響する可能性があります。特に、リッチテキストやホワイトボードのように更新頻度が高いデータでは注意が必要です。
そのため、スナップショット、履歴圧縮、ガベージコレクション、不要データの削除、同期範囲の分割などを設計する必要があります。CRDTライブラリが内部で最適化していても、自分のプロダクトの利用パターンでどれくらいデータが増えるかは必ず検証するべきです。CRDTの設計では、正しく同期できることだけでなく、長期運用できることも重要です。
16. CRDT導入チェックリスト
CRDTを導入する前に、本当に必要かどうかを確認することが重要です。CRDTは、複雑な同期問題に対して非常に強力な技術ですが、導入するとアプリ全体の設計が複雑になります。要件に合っていれば大きな価値がありますが、単純なアプリに入れると、保守コスト、デバッグコスト、データ管理コストだけが増える場合があります。
以下のチェックリストは、CRDT導入を検討するときに使えます。特に、同時編集、オフライン編集、複数端末同期、データ型、UI、セキュリティ、運用を確認することが重要です。
# CRDT導入チェックリスト
## 1. 要件
- 複数ユーザーが同じデータを同時に編集するか
- オフライン中の編集が必要か
- 複数端末でローカル編集を同期する必要があるか
- 中央サーバーに常時依存しない体験が必要か
## 2. データ
- 同期対象はテキストか、JSONか、リストか、集合か
- 同時更新時に自然なマージ結果を定義できるか
- 削除やundo/redoをどう扱うか
- データサイズが長期的に増えすぎないか
## 3. 同期
- WebSocket、WebRTC、P2P、サーバー同期のどれを使うか
- オフライン中の変更をどこに保存するか
- 再接続時にどう同期するか
- 同期状態をユーザーにどう見せるか
## 4. UI/UX
- 他ユーザーのカーソルやpresenceを表示するか
- マージ結果が不自然な場合にユーザーが修正できるか
- 変更履歴やバージョン管理が必要か
- 競合ではなく「共同編集」として自然に見せられるか
## 5. セキュリティ
- 誰が編集できるかをどう制御するか
- データを暗号化する必要があるか
- 端末紛失時にどう対応するか
- 削除要求やデータ保持期間に対応できるか
## 6. 運用
- デバッグ方法はあるか
- データサイズを監視できるか
- 同期失敗を検知できるか
- ライブラリの保守状況は問題ないか
16.1 まず要件を確認する
CRDT導入前に、同時編集やオフライン編集が本当に必要かを確認します。ユーザーが常にオンラインで、同じデータを同時に編集しないなら、CRDTは不要かもしれません。逆に、複数端末で同じメモを編集する、複数人が同じ文書を編集する、ネットワークが不安定な現場で入力する、といった要件があるなら、CRDTは有力な選択肢になります。
要件確認では、技術的な必要性だけでなく、ユーザー体験の価値も見ます。オフラインでも作業できることがユーザーにとって本当に重要なのか、同時編集がプロダクトの中心価値なのか、競合時に自動マージされることが望ましいのかを確認します。CRDTは、必要な場面では大きな価値を出しますが、不要な場面では複雑さになります。
16.2 ライブラリを評価する
CRDTを自作するのは非常に難しいため、多くの場合はYjsやAutomergeのようなライブラリを使います。ライブラリを選ぶときは、対応データ型、エディタ連携、永続化、同期プロトコル、パフォーマンス、データサイズ、ドキュメント、コミュニティ、ライセンスを確認する必要があります。
特に、リッチテキストやコードエディタで使う場合は、既存エディタとの連携実績が重要です。ProseMirror、Tiptap、CodeMirror、Monacoなどと連携できるかどうかは、開発コストに大きく影響します。また、サーバー側の同期構成、ローカル保存、IndexedDB対応、WebRTC対応、権限管理との組み合わせも確認するべきです。
16.3 小さく検証する
CRDTは、最初から大きなシステム全体に導入するより、小さなデータや一つの機能で検証するのが安全です。たとえば、共同メモ、コメント、ホワイトボードの一部、設定同期、簡単な共同リストなどから試すことができます。小さく始めることで、同期、データサイズ、UI表示、デバッグの難しさを早い段階で把握できます。
検証では、正常系だけでなく、同時編集、オフライン編集、再接続、複数端末、削除、undo/redo、同期失敗、権限変更、データ肥大化も確認する必要があります。デモではうまく動いても、実運用では長期間の履歴や複雑な編集パターンが問題になることがあります。CRDTは、実際の利用に近い条件で検証することが重要です。
17. CRDTでよくある誤解
CRDTにはいくつかの誤解があります。代表的なのは、「CRDTを使えば競合が完全になくなる」「CRDTならサーバーが不要になる」「CRDTなら常に最新状態が見える」「CRDTはどんなアプリにも向いている」といった考え方です。これらはすべて不正確です。
CRDTは、競合そのものを消す技術ではありません。競合が起きても、定義されたルールに従って自動的に収束させる技術です。また、サーバーなしの設計も可能ですが、実務では認証、永続化、同期中継、権限管理、監査ログのためにサーバーを使うことも多くあります。CRDTを正しく使うには、できることとできないことを理解する必要があります。
17.1 「Conflict-free」は競合ゼロではない
CRDTのConflict-freeは、ユーザーの意図が必ず衝突しないという意味ではありません。二人が同じ文章を同時に書き換えれば、意味的には衝突することがあります。CRDTが行うのは、その変更を定義された規則で統合し、全レプリカが同じ状態へ収束できるようにすることです。
そのため、CRDTを使っても、マージ結果が不自然になる可能性はあります。共同編集ツールでは、ユーザーが他者の変更を理解できるように、presence、カーソル、変更履歴、コメント、差分表示を用意することが重要です。技術的に収束することと、人間にとって納得できることは別です。
17.2 サーバーが完全に不要とは限らない
CRDTは中央サーバーなしでも同期できる設計に向いていますが、実務ではサーバーを使うことが多いです。サーバーは、同期中継、永続化、認証、権限管理、バックアップ、通知、監査ログなどを担当できます。特にビジネスアプリでは、誰がどのデータにアクセスできるかを管理する必要があります。
つまり、CRDTはサーバーを不要にする技術というより、サーバーに常時依存しなくてもローカル編集や分散同期を可能にする技術として考えると分かりやすいです。サーバーを使うかどうかは、プロダクト要件、セキュリティ、運用、コストによって決めるべきです。
17.3 強い整合性の代わりではない
CRDTは、強い整合性を置き換えるものではありません。CRDTは一時的な不一致を許容し、最終的な収束を目指します。これは、共同編集やオフライン編集では便利ですが、金融、在庫、医療、法的承認のように、常に厳密な状態が必要な領域には向かない場合があります。
このような領域では、CRDTを使うとしても、下書き、コメント、共同メモ、非正式な編集状態に限定するほうが安全です。最終確定、決済、承認、監査対象の状態は、サーバー側のトランザクションや厳密な整合性管理で扱うべき場合が多くあります。CRDTは強力ですが、適用範囲を見極めることが重要です。
おわりに
CRDTとは、Conflict-free Replicated Data Typeの略で、複数のレプリカが独立して更新されても、最終的に同じ状態へ収束できるように設計されたデータ型です。共同編集、オフラインファースト、Local-first Software、P2P同期、分散データベースなどで重要な技術です。複数人や複数端末が同じデータを扱う現代のアプリケーションにおいて、CRDTは非常に重要な選択肢になっています。
CRDTの強みは、ローカルで即時に編集できること、ネットワークが不安定でも作業を続けられること、後から変更をマージできることです。YjsやAutomergeのようなライブラリにより、リッチテキストエディタ、コードエディタ、ドキュメントアプリ、メモアプリなどでCRDTを使いやすくなっています。一方で、CRDTは万能ではなく、データサイズ、削除処理、意図保存、権限管理、デバッグ、強い整合性が必要な処理への不適合など、注意点も多くあります。
CRDTを導入するときは、同時編集やオフライン編集が本当に必要か、どのデータをCRDT化するか、どのライブラリを使うか、同期状態をUIでどう見せるかを慎重に設計する必要があります。CRDTは単なる技術トレンドではなく、プロダクトの体験設計、データ設計、運用設計に深く関わる技術です。正しく使えば、ユーザーがネットワークを意識せず、自然に共同作業できる強力なアプリ体験を作ることができます。
EN
JP
KR