私はGoogleAppEngineを使用しており、低レベルのJavaAPIを使用してBigTableにアクセスしています。私は4つのレイヤーでSAASアプリケーションを構築しています:
- クライアントWebブラウザ
- RESTfulリソースレイヤー
- ビジネスレイヤー
- データアクセス層
私は自分のモバイルオートディテーリング会社(およびそのような他の会社)の管理に役立つアプリケーションを構築しています。私はこれらの4つの別々の概念を表現する必要がありますが、私の現在の計画が良いものであるかどうかはわかりません。
- 予定
- ラインアイテム
- 請求書
- 支払い
アポイントメント:「アポイントメント」とは、サービスを提供するために従業員が期待される場所と時間です。
広告申込情報:「広告申込情報」とは、サービス、料金、割引、およびそれに関連する情報です。予定に入る可能性のある広告申込情報の例:
名前:価格:手数料:時間の見積もり フルディテール、レギュラーサイズ:160753.5時間 フルディテールクーポンが$10オフ:-1000時間 プレミアムディテール:2201104.5時間 派生合計(ラインアイテムではない):$ 370 $1858.0時間
請求書:「請求書」は、顧客が支払うことを約束した1つ以上の広告申込情報の記録です。
支払い:「支払い」は、どのような支払いが行われたかを記録したものです。
このアプリケーションの以前の実装では、作業はより単純であり、これらの4つの概念すべてをSQLデータベースの1つのテーブル「予定」として扱いました。1つの「予定」には、複数の広告申込情報、複数の支払い、および1つの請求書を含めることができます。請求書は、広告申込情報と顧客レコードから作成された単なる電子メールまたは印刷物でした。
10回のうち9回、これは正常に機能しました。1人の顧客が1台または数台の車両を1回予約し、自分で支払いをしたとき、すべてが壮大でした。しかし、このシステムは多くの条件下で機能しませんでした。例えば:
- 1人の顧客が1回の予約をしたが、途中で雨が降り、詳細担当者が翌日戻ってくる必要があった場合、2回の予約が必要でしたが、1つの広告申込情報、1つの請求書、1つの支払いのみでした。
- オフィスの顧客グループ全員が割引を受けるために同じ日に車を運転することに決めたとき、私は1つの予約が必要でしたが、複数の請求書と複数の支払いが必要でした。
- 1人の顧客が1回の小切手で2回の予約を支払った場合、2回の予約が必要でしたが、請求書と支払いは1回だけでした。
物事を少し混乱させることで、これらすべての外れ値を処理することができました。たとえば、詳細担当者が翌日に戻ってくる必要がある場合、2日目に「完了」という広告申込情報を使用して別の予定を立てると、費用は$0になります。または、1人の顧客が1回の小切手で2回の予約に対して支払いを行う場合、各予約に分割支払いレコードを入れます。これに伴う問題は、データの不一致の大きな機会を生み出すことです。データの不一致は、特に、顧客が1回の小切手で2回の予約を支払った、3番目の例などの財務情報が関係する場合に深刻な問題になる可能性があります。売掛金を適切に追跡するには、支払いを提供された商品やサービスと直接照合する必要があります。
提案された構造:
以下は、このデータを整理および保存するための正規化された構造です。おそらく私の経験不足のために、データの不一致エラーを回避するための優れた方法のように思われるため、データの正規化に重点を置いています。この構造により、他のテーブルの更新を気にすることなく、1回の操作でデータの変更を行うことができます。ただし、読み取りには、データのメモリ内編成と組み合わせた複数の読み取りが必要になる場合があります。後でわかりますが、パフォーマンスの問題がある場合は、「安全な」正規化された構造をそのままに保ちながら、クエリを高速化するために、いくつかの非正規化フィールドを「予定」に追加できます。非正規化により、書き込みが遅くなる可能性があります。
テーブル:
Appointment
start_time
etc...
Invoice
due_date
etc...
Payment
invoice_Key_List
amount_paid
etc...
Line_Item
appointment_Key_List
invoice_Key
name
price
etc...
以下は、特定の予定のリストに対して4つのエンティティ(テーブル)すべてを結び付けるために必要な一連のクエリと操作です。これには、各アポイントメントにスケジュールされたサービス、各アポイントメントの合計コスト、および各アポイントメントに対して受け取った未払いの天気に関する情報が含まれます。これは、予定のスケジューリングのためにカレンダーをロードするとき、またはマネージャーが操作の全体像を取得するときの一般的なクエリです。
- 「start_time」フィールドが指定された範囲内にある「Appointments」のリストのQUERY。
- 返された予定の各キーをリストに追加します。
- アポイントメント_キー_リストフィールドのすべての「Line_Items」のクエリには、返品アポイントメントのいずれかが含まれます
- すべてのラインアイテムの各invoice_keyをSetコレクションに追加します。
- 請求書セット内のすべての「請求書」のクエリ(これは、App Engineを使用した1回の非同期操作で実行できます)
- 返された請求書の各キーをリストに追加します
- invoice_key_listフィールドにあるすべての「支払い」のQUERYには、返された請求書のいずれかに一致するキーが含まれています
- 各予定が、予定されているline_items、合計価格、合計推定時間、および支払い済みかどうかを反映するように、メモリ内で再編成します。
...ご覧のとおり、この操作には4つのデータストアクエリとメモリ内の編成が必要です(メモリ内がかなり高速になることを願っています)
誰かがこのデザインについてコメントできますか?これは私が思いつくことができる最高のものですが、一般的に、または特にGAE(google app engine)の長所、短所、および機能の下で、より良いオプションまたは完全に異なるデザインがより適切に機能する可能性があると思います。 。
ありがとう!
使用法の明確化
ほとんどのアプリケーションは読み取りを多用し、一部のアプリケーションは書き込みを多用します。以下に、ユーザーが実行したい典型的なユースケースと内訳操作について説明します。
マネージャーは顧客から電話を受けます:
- 読み取り-マネージャーはカレンダーをロードし、利用可能な時間を探します
- 書き込み-マネージャーが顧客に情報を問い合わせます。これは、マネージャーが電話番号、名前、電子メール、住所などの各情報を入力するときの非同期読み取りの連続であると考えました。または、必要に応じて、おそらく1つクライアントアプリケーションがすべての情報を収集し、送信された後、最後に書き込みます。
- 書き込み-マネージャーは顧客のクレジットカード情報を削除し、別の操作として顧客のレコードに追加します
- 書き込み-マネージャーはクレジットカードに請求し、支払いが完了したことを確認します
マネージャーが電話をかけます。
- 読み取りマネージャーがカレンダーをロードします
- Read Managerは、電話をかけたい顧客の予定を読み込みます
- 書き込みマネージャーが[呼び出し]ボタンをクリックすると、呼び出しが開始され、新しいCallReacordエンティティが書き込まれます
- 読み取り呼び出しサーバーは呼び出し要求に応答し、CallRecordを読み取って呼び出しの処理方法を確認します
- Write Callサーバーは、更新された情報をCallRecordに書き込みます
- 呼び出しが閉じられたときに書き込み、呼び出しサーバーは、CallRecordリソースを更新するためにサーバーに別の要求を行います(注:この要求はタイムクリティカルではありません)
受け入れられた回答:: 上位2つの回答はどちらも非常に思慮深く、高く評価されました。私は、彼らの露出を可能な限り均等にするために、投票数の少ないものを受け入れました。