楽観的なUI、CQRS、およびEventSourcing

楽観的なUI、CQRS、およびEventSourcing







負荷の高いWebアプリケーションを開発する場合、CQRSなどの原則がスケーリングを改善するためにしばしば適用されます。 メソッドは、アクションを実行するコマンドまたはデータを返すクエリのいずれかでなければならないが、同時に両方であってはならないことを示しています。 言い換えれば、システムに対する質問は答えを変えてはなりません。 より正式には、副作用のない純粋なメソッドにのみ値を返すことができます。







ただし、適切なスケーリングを行うには、APIの読み取り/書き込みの分離だけでは不十分です。 また、このAPIが機能するデータベースを分離する必要があります。 次に、EventSourcingが役立ちます。 彼は、すべてのシステムイベントを1つのデータベースに格納し、EventStoreと呼び、それに基づいて他のすべてのデータベースとテーブルを構築することを提案します。







CQRSとEventSourcingの組み合わせは、システム内の負荷、ノードの数、補助データベースの数、キャッシュの使用などのバランスをとる観点から大きく手を緩めますが、同時にアプリケーションのロジックを複雑にし、多くの制限を導入します。







この記事では、このようなシステムのクライアント部分を設計する際のニュアンスの1つ、つまりUIの楽観的な更新について検討します。







フロントエンドについては、流行のReactとReduxを取り上げましょう。 ところで、ReduxとEventSourcingは非常によく似たテクノロジーです。







楽観的なユーザーインターフェイスの更新は実装が容易ではなく、CQRSとEventSourcingによりタスクがさらに難しくなります。







これはどのように機能しますか? 段階的に理解しましょう。







  1. コマンドを送信し、応答を待たずにReduxストアで楽観的なイベントをディスパッチします。 楽観的なイベントには、予想されるサーバー結果が含まれます。 また、このステップでは、イベントが変更されるデータの現在の状態を記憶しています。







  2. チームを派遣した結果を待っています。 コマンドが失敗した場合、イベントディスパッチは、最初の手順で記憶したデータに基づいて楽観的な更新をロールバックします。 すべてが順調であれば、何もしません。







  3. 実際のイベントがバスからクライアントに到着するのを待っています。 これが発生したら、楽観的な更新をロールバックし、実際のイベントを適用します。


実際にどのように見えるか:







成功 失敗
楽観的成功楽観的な失敗
楽観的成功-redux楽観的失敗-redux


楽観的な更新コードをReduxストアのミドルウェアとして説明します。







const optimisticCalculateNextHashMiddleware = (store) => { const tempHashes = {}; const api = createApi(store); return next => action => { switch (action.type) { case SEND_COMMAND_UPDATE_HASH_REQUEST: { const { aggregateId, hash } = action; // Save the previous data const { hashes } = store.getState() const prevHash = hashes[aggregateId].hash; tempHashes[aggregateId] = prevHash // Dispatch an optimistic action store.dispatch({ type: OPTIMISTIC_HASH_UPDATED, aggregateId, hash }); // Send a command api.sendCommandCalculateNextHash(aggregateId, hash) .then( () => store.dispatch({ type: SEND_COMMAND_UPDATE_HASH_SUCCESS, aggregateId, hash }) ) .catch( (err) => store.dispatch({ type: SEND_COMMAND_UPDATE_HASH_FAILURE, aggregateId, hash }) ); break; } case SEND_COMMAND_UPDATE_HASH_FAILURE: { const { aggregateId } = action; const hash = tempHashes[aggregateId]; delete tempHashes[aggregateId]; store.dispatch({ type: OPTIMISTIC_ROLLBACK_HASH_UPDATED, aggregateId, hash }); break; } case HASH_UPDATED: { const { aggregateId } = action; const hash = tempHashes[aggregateId]; delete tempHashes[aggregateId]; store.dispatch({ type: OPTIMISTIC_ROLLBACK_HASH_UPDATED, aggregateId, hash }); break; } } next(action); } }
      
      





すべてがどのように機能するかをライブで確認できます:









おわりに



UIの楽観的な更新により、アプリケーションの応答性が大幅に向上します。 賢明に、そして細心の注意を払って使用する必要がありますが。 場合によっては、データ損失につながり、ユーザーインターフェイスの理解を複雑にする可能性があります。 たとえば、写真の下のような楽観的な見方は良く、支払いの楽観的な形は悪いです。 だからfireを壊さないでください。 頑張って








All Articles