私は現在、CQRS/ES、Docker、AMQP、およびそれに伴うその他すべての利点を学ぶために、マイクロサービスベースのシステムを構築しています。
私はオンラインで質問をしたことがありません。私は通常、他の人からの質問を読んで自分の質問に対する回答を見つけるのが得意なので、.
今回、私はとてもシンプルに見えることに困惑しています。
請求書、注文書、クレジットメモなどのビジネス文書には通常、識別番号が付いています。 (つまり、請求書番号:5707)
私の質問は、イベントソースシステムでこれらの種類のドキュメントのそれぞれにどのように連番を生成するかです。
請求書番号が誤って重複したりスキップされたりする可能性がある競合状態について心配しています。これのベストプラクティスは何ですか?
お時間をいただきありがとうございます。
編集:私が試したものは次のとおりです:
Invoice
という関連するサガがあるnewInvoiceInitialization
という集約ルート(AR)があります。 Invoice
はコマンドCreateNewInvoice
を受け取ります。 Invoice
ARはこのコマンドを処理し、NewInvoiceWasCreated
というイベントを発行します。
newInvoiceInitialization
サガはNewInvoiceWasCreated
イベントを処理し、サガを開始して請求書ARを初期化します。 newInvoiceInitialization
sagaは、CheckoutNextInvoiceNumber
コマンドをSequenceNumberGenerator
という別のARに送信し、値オブジェクトをCorrelationId
およびResourceTypeName
とします。
SequenceNumberGenerator
ARはSequenceNumberCheckoutSaga
と呼ばれるサガで動作します。これら2つは、シーケンスの次のInvoiceNumber
を「チェックアウト」し、Invoice
イベントを介してSequenceNumberWasCheckoutOut
ARに提供します。
newInvoiceInitialization
サガはSequenceNumberWasCheckoutOut
イベントを受信し、InvoiceNumber
コマンドを使用してInvoice
をAssignInvoiceNumberToInvoice
ARに送信します。
Invoice
ARがnewInvoiceInitialization
サガを正常に完了すると、イベントInvoiceNumberWasAssignedToInvoice
が発行されます。このイベントはSequenceNumberCheckoutSaga
をトリガーして、FinalizeNumberCheckout
ARにコマンドSequenceNumberGenerator
を提供し、SequenceNumberCheckoutSaga
を終了してその数の使用を確定します。
Invoice
がInvoiceNumber
を受け入れることができないか、その他の問題がある場合、InvoiceNumberAssignmentFailed
イベントを送信してSequenceNumberCheckoutSaga
にSequenceNumberGenerator
をコマンドさせます。 ARでResetNumberCheckout
コマンドを使用して、チェックアウトシーケンス番号をロールバックし、SequenceNumberCheckoutSaga
を終了します。
これはすべて、見落とされたり重複したりしない番号の請求書を作成するのは非常に複雑に思えます。私はおそらく何かを逃している。
TL; DRは次のとおりです。そうです、衝突を防ぐには技術的な課題がありますが、これは実際にビジネス部門に問い合わせる必要があります。
ビジネスの観点からのベストプラクティスは連番ではなく、何らかの構造を含むものです。構造が適切に選択されている場合、ほとんどの競合状態を回避するのに役立ちます。
1つの例は、番号付けにタイムスタンプを使用することです。通常は逆の順序で、単純なファイル名の並べ替えが適切な順序を提供します。YYYYMMDD_document_title
。
私が使用中に見たもう1つのことは、ケースや事業年度に従ってドキュメントに番号を付けることです。ケース番号はお客様番号の場合があります:<case-or-customer>/<year>/<sequence number>
。
任意のシーケンスに名前空間識別子のプレフィックスを追加できます。つまり、顧客の請求書番号が通信とは無関係に番号付けされる場合があります。
各ビジネス部門は、自分が最もよく機能するシステムを選択します。そのため、この番号の作成方法を説明するかどうかは、彼ら次第です。
とはいえ、このような方法で数値を作成しても、衝突が発生する可能性があります。ソリューションの一部としてREDISについて言及しています。 REDISを使用して 原子的に増分するカウンター を作成できます。
あなたにとって最良のパターンは次のようになると思います:
<prefix>-<counter>-<title>
。ユーザーが読み取り可能なあらゆる種類のIDとしてDB IDを直接使用しないことを強くお勧めします。
すべての一意の識別子は、1つのシステムにのみ従う必要があります。ユーザーが目にする場所にDB IDを公開すると、システムをいじる理由がわかります。 Steve Jobsは、彼が2番だと知ったとき、別の従業員ID番号を持っていると有名に主張しました。Wazから1番を取るのではなく、従業員0番になるように要求しました。ユーザーにこれらの番号を見せないでください。システムの低レベル。単純なマッピングでこの問題を解決でき、DB IDがユーザーに表示されないようにすることができました。
請求書番号は、単なるDB IDのように聞こえます。理由はここにありません。一意性はスコープ内でのみ意味があることを理解する必要があります。 DB IDは、DBテーブルに固有です。 DBへ。ただし、請求書はビジネスに固有のものです。一部の企業には複数のDBがあります。拡張や成長に伴い、他のDBを取得する人もいます。彼らが他の会社を買うとき、いくつかは他のDBを手に入れます。
このような変化を予測できるふりをしないでください。テーブルと直接の関係を持つ必要がないものからIDを隠すことにより、テーブルの一意性を維持します。請求書のような他のことは、それらの他のものが何であるかを理解する何かを通過させる。その翻訳はビジネスルールです。そのビジネスルールをDBから除外します。
この方法では、他の会社を購入した場合、またはDBが気にしないため他の会社が購入した場合に、DBが機能します。
これまでのところ気分が良ければ、私はあなたが正しい問題を回避しました。私が持っています。それがポイントでした。ただし、ビジネスルールの計画を立ててほしい場合は、一意性を確保するためにさらに要素を追加する必要があるため、拡張できる請求書番号を検討してください。次に、複合キーのように、競合が発生したときに、請求書の残りの部分を以前のスコープに配置するタグを追加することで、競合を解消できます。これは、これらの新しいタグが何であるかを知るまで、これらの新しいタグがどうなるかについての構造が課されないため、これらの競合が何であるかを予測しようとすることとは大きく異なります。これまでに行ったことは、請求書番号を拡張する必要があるかもしれないことを認識しているだけです。これは、以前の会社の文字を使用するのと同じくらい簡単で、ダッシュのような区切り文字や、「グループ」のような新しいフィールドを許可します(これは一部の保険会社がそれと呼んでいます)。
あなたが考えているなら:ああ、私はあなたが本当にポイントを逃している会社の構造を明らかにするだけです。カバーの下で何かがどのように機能するかを示す必要はありません。カバーの下にあるものが変更されない場合でも、ニーズの変化に応じて、表示するものを変更できるようにする必要があります。
設計でこの拡張が可能である限り、それを予測する必要はありません。どのような形の変化が起こるかを予測し、それを誤解することは、それを放っておくよりも多くの損害をもたらします。直接の露出を最小限に抑えるだけで、作業変更の原因も最小限に抑えられます。