オンラむンオヌクションプラットフォヌムを䜜成するためのCQRSおよびむベント゜ヌシングの適甚

同僚、こんにちは私はミシャです。プログラマヌずしお働いおいたす。



この蚘事では、私たちのチヌムがCQRSずむベント゜ヌシングのアプロヌチをオンラむンオヌクションサむトであるプロゞェクトに適甚するこずを決定した方法に぀いおお話したす。そしお、これから䜕が埗られたのか、私たちの経隓からどのような結論を導き出すこずができるのか、そしおどのレヌキがCQRSずESを利甚する人のために螏たないこずが重芁であるのかに぀いおもです。

画像





プレリュヌド



たず、少し歎史ずビゞネスの背景。顧客は、いわゆるタむムドオヌクションを開催するためのプラットフォヌムを持っお私たちのずころにやっお来たした。このプラットフォヌムは、すでに生産されおいお、ある皋床のフィヌドバックが収集されたした。クラむアントは、ラむブオヌクション甚のプラットフォヌムを䜜成するこずを望んでいたした。



今少し甚語。オヌクションずは、特定のアむテムが販売されたずき-ロットであり、バむダヌ入札者が入札したす。ロットの所有者は、最も高い入札をしたバむダヌです。時限オヌクションは、各ロットに事前に決められた締め切り時刻がある堎合です。バむダヌは賭けをしたす。ある時点で、ロットは閉鎖されたす。 ebayに䌌おいたす。



時限プラットフォヌムは、CRUDを䜿甚しお叀兞的な方法で䜜成されたした。ロットは、予定通りに開始され、別のアプリケヌションによっおクロヌズされたした。これらすべおは非垞に確実に機胜したせんでしたいく぀かの賭けは倱われたした、いく぀かは間違った買い手に代わっお、ロットが閉じられなかったか、数回閉じられたように行われたした。



ラむブオヌクションは、むンタヌネットを介しおリモヌトで実際のオフラむンオヌクションに参加する機䌚です。郚屋内郚甚語では「郚屋」があり、ハンマヌず聎衆がいるオヌクションのホストが含たれおいたす。ラップトップのすぐ隣にいわゆる事務員が座っおいたす。事務員は、むンタヌフェむスのボタンを抌すず、オヌクションのコヌスをむンタヌネットにブロヌドキャストし、接続されおいたす。オヌクションの時点たでに、バむダヌはオフラむンになっおいる入札を確認し、入札を行うこずができたす。



どちらのプラットフォヌムも原則ずしおリアルタむムで機胜したすが、時間制限のあるすべおの買い物客が同じ立堎にある堎合、ラむブの堎合はオンラむンの買い物客が宀内の買い物客ずうたく競争できるこずが非垞に重芁です。぀たり、システムは非垞に高速で信頌性の高いものでなければなりたせん。時限プラットフォヌムの悲しい経隓は、叀兞的なCRUDは私たちに適しおいないこずをはっきりず語っおいたす。



私たちには、CQRSずESを䜿甚した独自の経隓がなかったため、CQRSずESを䜿甚しおいる同僚私たちは倧䌁業ですに盞談し、圌らにビゞネスの珟実を瀺し、共同でCQRSずESが私たちに適しおいるずいう結論に達したした。



他にオンラむンオヌクションの詳现は䜕ですか。



  • — . , « », , . — , 5 . .
  • , , .
  • — - , , — .
  • , .
  • ゜リュヌションはスケヌラブルでなければなりたせん-耇数のオヌクションを同時に開催できたす。


CQRSずESのアプロヌチの抂芁



CQRSずESのアプロヌチの怜蚎に぀いおは詳しく説明したせん。これに぀いおはむンタヌネット、特にハブレに関する資料がありたすたずえば、ここCQRS +むベント゜ヌシングの抂芁。ただし、䞻なポむントに぀いお簡単に説明したす。



  • むベント゜ヌシングで最も重芁なこずシステムはデヌタを保存したせんが、倉曎の履歎、぀たりむベントを保存したす。システムの珟圚の状態は、むベントの順次適甚によっお取埗されたす。
  • ドメむンモデルは、集合䜓ず呌ばれる゚ンティティに分割されたす。ナニットにはバヌゞョンがありたす。むベントは集蚈に適甚されたす。集玄にむベントを適甚するず、バヌゞョンが増加したす。
  • write-. , .
  • . . , , . «» . .
  • , , - ( N- ) . «» . , .
  • - , , , , write-.
  • write-, read-, , . read- . Read- .
  • , — Command Query Responsibility Segregation (CQRS): , , write-; , , read-.






. .





時間を節玄するため、たた特定の経隓がないため、CQRSずESには䜕らかのフレヌムワヌクを䜿甚する必芁があるず刀断したした。



䞀般的に、私たちの技術スタックはMicrosoft、぀たり.NETずCです。デヌタベヌス-Microsoft SQL Server。すべおがAzureでホストされおいたす。このスタックで時限プラットフォヌムが䜜成されたした。その䞊にラむブプラットフォヌムを䜜成するこずは論理的でした。



圓時、私が芚えおいるように、技術スタックに関しおは、チンチラがほずんど唯䞀の遞択肢でした。それで私たちは圌女を連れお行きたした。



なぜCQRSずESフレヌムワヌクが必芁なのですか圌は「そのたた」そのような問題を解決し、次のような実装の偎面をサポヌトできたす。



  • ゚ンティティ、コマンド、むベントの集玄、バヌゞョン管理の集玄、再氎和、スナップショットのメカニズム。
  • さたざたなDBMSを操䜜するためのむンタヌフェヌス。むベントずアグリゲヌトのスナップショットを曞き蟌みベヌスむベントストアに保存/ロヌドしたす。
  • キュヌを操䜜するためのむンタヌフェヌス-コマンドずむベントを適切なキュヌに送信し、キュヌからコマンドずむベントを読み取りたす。
  • WebSocketを操䜜するためのむンタヌフェヌス。


したがっお、チンチラの䜿甚を考慮しお、スタックに远加したした。



  • コマンドおよびむベントバスずしおのAzure Service BusであるChinchillaは、そのたたでサポヌトしたす。
  • 曞き蟌みおよび読み取りデヌタベヌスはMicrosoft SQL Serverです。぀たり、どちらもSQLデヌタベヌスです。これは意識的な遞択の結果ではなく、歎史的な理由によるずは蚀いたせん。


はい、フロント゚ンドはAngularで䜜成されおいたす。



すでに述べたように、システムの芁件の1぀は、ナヌザヌが自分のアクションの結果ず他のナヌザヌのアクションに぀いおできるだけ早く孊習するこずです。これは、顧客ず店員の䞡方に圓おはたりたす。したがっお、SignalRずwebsocketを䜿甚しお、フロント゚ンドのデヌタをすばやく曎新したす。ChinchillaはSignalR統合をサポヌトしおいたす。



ナニットの遞択



CQRSずESのアプロヌチを実装するずきに最初に行うこずの1぀は、ドメむンモデルを集玄に分割する方法を決定するこずです。



私たちの堎合、ドメむンモデルは次のようないく぀かの䞻芁な゚ンティティで構成されおいたす。



public class Auction
{
     public AuctionState State { get; private set; }
     public Guid? CurrentLotId { get; private set; }
     public List<Guid> Lots { get; }
}

public class Lot
{
     public Guid? AuctionId { get; private set; }
     public LotState State { get; private set; }
     public decimal NextBid { get; private set; }
     public Stack<Bid> Bids { get; }
}
 
public class Bid
{
     public decimal Amount { get; set; }
     public Guid? BidderId { get; set; }
}




2぀の集蚈、オヌクションずロット入札ありがありたした。䞀般的に、それは論理的ですが、1぀のこずは考慮しおいたせんでした。このような分割により、システムの状態は2぀のナニットに分散され、堎合によっおは、䞀貫性を維持するために、1぀ではなく䞡方のナニットに倉曎を加える必芁がありたす。たずえば、オヌクションを䞀時停止できたす。オヌクションが䞀時停止されおいる堎合は、入札できたせん。ロット自䜓を䞀時停止するこずは可胜ですが、䞀時停止されたオヌクションは「䞀時停止解陀」以倖のコマンドを凊理できたせん。



あるいは、1぀の集合䜓、オヌクション、すべおのロットず入札を䜜成するこずもできたす。しかし、オヌクションには最倧数千のロットがあり、1぀のロットに数十の入札があるため、そのようなオブゞェクトは非垞に困難になりたす。オヌクションの存続期間䞭、このようなアグリゲヌトには倚くのバヌゞョンがあり、アグリゲヌトのスナップショットが䜜成されおいない堎合、そのようなアグリゲヌトの再氎和すべおのむベントのアグリゲヌトぞの順次適甚には、かなり長い時間がかかりたす。これは私たちの状況には受け入れられたせん。スナップショットを䜿甚する堎合私たちはスナップショットを䜿甚したす、スナップショット自䜓が非垞に重くなりたす。



䞀方、単䞀のナヌザヌアクションの凊理内で2぀のアグリゲヌトに倉曎が確実に適甚されるようにするには、トランザクションを䜿甚しお同じコマンド内で䞡方のアグリゲヌトを倉曎するか、同じトランザクション内で2぀のコマンドを実行する必芁がありたす。どちらも抂しお、アヌキテクチャ違反です。



ドメむンモデルを集合䜓に分解するずきは、このような状況を考慮する必芁がありたす。



プロゞェクトの進化のこの段階では、AuctionずLotずいう2぀のナニットがあり、いく぀かのコマンド内で䞡方のナニットを倉曎するこずでアヌキテクチャを壊しおいたす。



ナニットの特定のバヌゞョンにコマンドを適甚する



耇数のバむダヌが同時に同じロットに入札した堎合、぀たり、「入札」コマンドをシステムに送信した堎合、1぀の入札のみが成功したす。倚くは集合䜓であり、バヌゞョンがありたす。コマンドの凊理䞭に、むベントが生成され、それぞれが集玄のバヌゞョンを増分したす。2぀の方法がありたす。



  • 適甚するアグリゲヌトのバヌゞョンを指定するコマンドを送信したす。その埌、コマンドハンドラは、コマンドのバヌゞョンをナニットの珟圚のバヌゞョンずすぐに比范し、䞀臎しない堎合は続行できたせん。
  • コマンドでナニットのバヌゞョンを指定しないでください。次に、集蚈がいく぀かのバヌゞョンで再氎和され、察応するビゞネスロゞックが実行され、むベントが生成されたす。そしお、それらが保存されたずきのみ、そのようなバヌゞョンのナニットがすでに存圚するずいう実行ポップアップが衚瀺されたす。他の誰かが以前にやったからです。


2番目のオプションを䜿甚したす。これにより、チヌムは実行される可胜性が高くなりたす。コマンドを送信するアプリケヌションの郚分この堎合、これはフロント゚ンドですのため、ある確率で、アグリゲヌトの珟圚のバヌゞョンは、バック゚ンドの実際のバヌゞョンよりも遅れたす。特に倧量のコマンドが送信され、ナニットのバヌゞョンが頻繁に倉曎される状況では。



キュヌを䜿甚しおコマンドを実行するずきの゚ラヌ



私たちの実装では、チンチラによっお倧きく駆動され、コマンドハンドラヌがキュヌMicrosoft Azure Service Busからコマンドを読み取りたす。チヌムが技術的な理由タむムアりト、キュヌ/ベヌスぞの接続゚ラヌで倱敗した堎合ず、ビゞネス䞊の理由すでに受け入れられおいるのず同じ金額で倚くの入札を詊みた堎合などを明確に区別したす。最初のケヌスでは、コマンドはキュヌ蚭定で指定された繰り返し回数に達するたでコマンドの実行が繰り返され、その埌コマンドはデッドレタヌキュヌAzure Service Busでの未凊理メッセヌゞの別のトピックに送信されたす。業務執行の堎合、チヌムは即座にデッドレタヌキュヌに送られたす。







キュヌを䜿甚しおむベントを凊理するずきの゚ラヌ



コマンドの実行の結果ずしお生成されたむベントは、実装に応じお、むベントハンドラヌによっおキュヌに送信され、キュヌから取埗されたす。たた、むベントを凊理するずきに゚ラヌも発生したす。



ただし、コマンドが実行されない状況ずは察照的に、ここではすべおが悪化しおいたす。コマンドが実行され、むベントが曞き蟌みベヌスに曞き蟌たれたが、ハンドラヌによる凊理が倱敗した可胜性がありたす。たた、これらのハンドラヌの1぀が読み取りベヌスを曎新しおも、読み取りベヌスは曎新されたせん。぀たり、䞍敎合な状態になりたす。読み取りむベントを再詊行するメカニズムのため、ほずんどの堎合、デヌタベヌスは最埌に曎新されたすが、すべおの詊行埌にデヌタベヌスが壊れたたたになる可胜性はただ残っおいたす。







私たちはこの問題を家で経隓したした。しかし、その理由は、むベント凊理にいく぀かのビゞネスロゞックがあり、激しい賭けで時々倱敗する可胜性が高いためです。残念ながら、これは遅すぎるこずに気づき、ビゞネス実装を迅速か぀簡単にやり盎すこずは䞍可胜でした。



その結果、䞀時的な察策ずしお、アプリケヌションの曞き蟌み郚分から読み取り郚分にむベントを転送するためのAzure Service Busの䜿甚を停止したした。代わりに、いわゆるむンメモリバスが䜿甚されたす。これにより、コマンドずむベントを1぀のトランザクションで凊理し、障害が発生した堎合はすべおをロヌルバックできたす。







このような゜リュヌションはスケヌラビリティには貢献したせんが、䞀方で、読み取りベヌスが壊れおフロント゚ンドが故障し、すべおのむベントを再生しお読み取りベヌスを再䜜成しないずオヌクションを続行できなくなる状況は陀倖したす。



むベントに応答しおコマンドを送信する



これは原則ずしお適切ですが、この2番目のコマンドの実行に倱敗しおもシステムの状態が壊れない堎合に限られたす。



1぀のコマンドの耇数のむベントを凊理する



䞀般に、1぀のコマンドを実行するず、いく぀かのむベントが発生したす。むベントごずに、読み取りデヌタベヌスに倉曎を加える必芁がありたす。たた、むベントのシヌケンスも重芁であり、間違ったシヌケンスでは、むベント凊理が正垞に機胜したせん。これはすべお、たずえば、キュ​​ヌからメッセヌゞを読み取るコヌドの異なるむンスタンスを䜿甚しお、キュヌから読み取り、1぀のコマンドのむベントを個別に凊理するこずができないこずを意味したす。さらに、キュヌからのむベントがそこに送信されたのず同じ順序で読み取られるこずが保蚌されおいる必芁がありたす。たたは、最初の詊行ですべおのコマンドむベントが正垞に凊理されるわけではないずいう事実に備える必芁がありたす。







耇数のハンドラヌで1぀のむベントを凊理する



システムが1぀のむベントに応答しお耇数の異なるアクションを実行する必芁がある堎合、通垞、このむベントの耇数のハンドラヌが䜜成されたす。それらは、䞊行しおたたは順次に動䜜できたす。順次起動の堎合、ハンドラヌの1぀が倱敗するず、シヌケンス党䜓が再開されたすこれはチンチラの堎合です。このような実装では、䞀床正垞に実行されたハンドラヌの2回目の実行が倱敗しないように、ハンドラヌがべき等であるこずが重芁です。そうしないず、2番目のハンドラヌがチェヌンから萜ちたずきに、最初のハンドラヌが2回目およびそれ以降の詊行で萜ちるため、チェヌンチェヌンは完党に機胜したせん。



たずえば、読み取りベヌスのむベントハンドラヌは、5ルヌブルの倚くに入札を远加したす。これを行う最初の詊みは成功し、2回目はデヌタベヌスで制玄を実行できたせん。







結論/結論



珟圚、私たちのプロゞェクトは、私たちに思われるように、私たちのビゞネスの詳现に関連する既存のレヌキのほずんどにすでに螏み蟌んだ段階にありたす。䞀般的に、私たちの経隓はかなり成功しおいるず考えおいたす。CQRSずESは、この分野に適しおいたす。チンチラの攟棄により、プロゞェクトのさらなる発展が芋られ、より柔軟な別のフレヌムワヌクが支持されたした。ただし、フレヌムワヌクの䜿甚をたったく拒吊するこずもできたす。たた、䞀方では信頌性ず、他方では゜リュヌションの速床ずスケヌラビリティずの間のバランスを芋぀ける方向にいく぀かの倉曎が生じる可胜性がありたす。



ビゞネスコンポヌネントに関しおは、ここでもいく぀かの質問が未解決のたたです。たずえば、ドメむンモデルを集蚈に分割したす。



私たちの経隓が誰かのために圹立ち、時間を節玄し、熊手を避けるのに圹立぀こずを願っおいたす。ご枅聎ありがずうございたした。



All Articles