ある種のエンティティを格納できるバックエンドがあるとしましょう。また、これらのエンティティを作成、読み取り、変更、削除するためのAPIがあり、CRUDと略されます。しかし、APIはサーバー上にあり、ユーザーはどこかに深く入り込み、リクエストの半分はタイムアウトになります。エンドレスのプリローダーを表示したくないので、通常はユーザーの操作をブロックします。オフラインでは、最初にキャッシュからアプリケーションをロードすることを想定しているので、データはそこから取得する必要がありますか?
すべてのデータをIndexedDBに保存し(データの数がそれほど多くないとしましょう)、可能であればサーバーと同期することをお勧めします。いくつかの問題が発生します。
エンティティのIDがサーバー上、データベース内で生成されている場合、サーバーが利用できないときにIDなしで生活するにはどうすればよいですか?
サーバーと同期する場合、クライアントで作成されたエンティティと、別のユーザーによってサーバーで削除されたエンティティを区別するにはどうすればよいですか?
競合を解決する方法は?
識別
識別子が必要なので、自分で作成します。これには、GUIDまたは `+ new Date()`で問題ありませんが、いくつかの注意点があります。サーバーから実際のIDで応答があった場合にのみ、どこでもそれを置き換える必要があります。この新しく作成されたエンティティがすでに他の人から参照されている場合は、これらのリンクも修正する必要があります。
同期
車輪の再発明はしません。データベースレプリケーションを見てみましょう。火のように延々と見ることができますが、要するに、オプションの1つは次のようになります。エンティティをIndexedDBに保存することに加えて、変更のログを書き込みます:[time、 'update'、Id = 3 、Name = 'Ivan']、[time、 'create'、Name = 'Ivan'、Surname = 'Petrov']、[time、 'delete'、Id = 3] .. ..
, . , , IndexedDB. Id.
- , , . , - , . - , , . , : , , , . Eventual Consistency.
, , . Operational Transformations (OT) Conflict-free Replicated Data Types (CRDT) . , CRDT : UpdatedAt . , .
, Id . , . , , . . , , Id , . - . . , . Last write win. Eventual Consistency: , . .
function mergeLogs(left, right){
const ids = new Set([
...left.map(x => x.id),
...right.map(x => x.id)
]);
return [...ids].map(id => mergeIdLogs(
left.filter(x => x.id == id),
right.filter(x => x.id ==id)
)).reduce((a,b) => ({
left: [...a.left, ...b.left],
right: [...a.right, ...b.right]
}), {left: [], right: []});
}
function mergeIdLogs(left,right){
const isWin = log => log.some(x => ['create','delete'].includes(x.type));
const getMaxUpdate = log => Math.max(...log.map(x => +x.updatedAt));
if (isWin(left))
return {left: [], right: left};
if (isWin(right))
return {left: right, right: []};
if (getMaxUpdate(left) > getMaxUpdate(right))
return {left: [], right: left};
else
return {left: right, right: []};
}
それぞれの特定のケースで詳細に悪魔があり、概して、ここで実装するものは何もないため、実装はありません-識別子の生成とindexedDBへの書き込み。
もちろん、CRDTまたはOTの方が優れていますが、迅速に行う必要があるが、バックエンドで許可されていない場合は、この作業で十分です。