質問レビュー
@leanneが言ったように、あなたはSubscription
その専門分野が、たとえば、MonthlySubscription
(ComplimentarySubscription
この答えの名前を付けるために)であるモデルを作成しています。
サブスクリプションが期限切れになる可能性があることを知っています:
- の場合、
MonthlySubscription
これはユーザーが今月のサブスクリプションを支払わなかった場合に発生します
- の場合
ComplimentarySubscription
、有効期限はユーザーに割り当てられたときに割り当てられます
ご覧のとおり、ExpirationDate
はすべての重要な属性ですSubscription
が、保存方法はそれぞれ異なります。前者の場合は最後のイベントに基づいて計算する必要がありますが、後者の場合は直接取得できます。
データベースでの継承の処理
したがって、このサンプルモデルをデータベーススキーマにマップするには、 MartinFowlerの『 Patterns of Enterprise Application Architecture 』の本で説明されているクラステーブル継承パターンを使用できます。その意図は次のとおりです。
「クラスごとに1つのテーブルを持つクラスの継承階層を表します」。
基本的に、クラス間で共通の属性を共有するテーブルがあり、各クラスに固有の属性を別々のテーブルに格納します。
これを念頭に置いて、提案したオプションを確認しましょう。
complimentary_subscription
外部キーとしてユーザーIDを持つ別のテーブルがありますか?
特定の詳細を格納するための個別のテーブルComplimentarySubscription
があることは良いことのように聞こえますが、これをテーブルに関連付けないと、aと。のsubscription
両方を持つユーザーになってしまう可能性があります。その外部キーは、ユーザーがサブスクリプションを持っているかどうかを示すテーブルを指している必要があります(ユーザーごとに最大1つのサブスクリプションを適用する必要があります)。MonthlySubscription
ComplimentarySubscription
subscription
- それらの特別な「サブスクリプション」を
subscription
?に記録します。
はい、ユーザーが毎月または無料でサブスクリプションを持っていることを記録する必要があります。ただし、金額がゼロの特別なサブスクリプションを記録するようなことを考えている場合は、適切なモデルを検索するのではなく、現在の設計に一致するソリューションを探しています(そうでない場合もあります)。
is_complimentary
または、やcomplimentary_expires_date
?などの列のユーザー行に別の列を追加します。
個人的には、これが属していない場所に情報を入れているので、これは好きではありません。それを考慮して、無料サブスクリプションの有効期限をどこに保存しますか(月額サブスクリプションの場合は、有効期限を保存せずに計算していることに注意してください)。そのすべての情報がそれ自身の「家」を求めているようです。また、後で新しいタイプのサブスクリプションを追加する必要がある場合、そのテーブルは乱雑になり始めます。
expires
より一般的な列をユーザー行に追加しますか?
これを行う場合、が変更されるたびにデータ同期を処理する必要がありますsubscription_event
(月額サブスクリプションの場合)。通常、私はこのデータ重複の状況を回避しようとします。
サンプルソリューション
新しいタイプのサブスクリプションを追加するときに拡張性を優先するために私が行うことは、との間でsubscription
共有される詳細を格納するテーブルを用意し、行が関連するサブスクリプションの種類を区別できる列キーを追加することです。MonthlySubscripton
ComplimentarySubscription
type
subscription
次に、親行を参照して、各サブスクリプションタイプに固有の詳細を独自のテーブルに格納します。
Subscription
データを取得するには、行の特定のtype
列値の正しいタイプのインスタンス化を担当するオブジェクトが必要ですsubscription
。
type
列の値を定義する方法、マッパーオブジェクトを使用してSubscription
インスタンス化を行う方法などの詳細については、「エンタープライズアプリケーションアーキテクチャのパターン」の本のパターンを参照してください。
2012年1月3日更新:type
列を定義および処理するための代替手段
コメントに@enoinocによって投稿された次の質問を明確にするための更新があります。
特に提案された列の場合、これは、支払いなしで期限切れになるまでの月数など、さまざまなタイプのサブスクリプションを説明type
するテーブルを指す外部キーである可能性があります。Plans
それは論理的に聞こえますか?
Plans
編集する必要のない静的な情報でない限り、その情報をテーブルに含めてもかまいません。その場合は、ソリューションを過度に一般化して、その知識を対応するSubscription
サブクラスに入れないでください。
外部キーアプローチについて、私はそのように進むいくつかの欠点を考えることができます:
- 目標は、テーブル
Subscription
の各行に使用するサブクラスを知ることであることを忘れないでください。Plans
取得したのが外部キー値(たとえば整数)だけの場合は、その値を使用するクラスにマップするコードを作成する必要があります。それはあなたのために余分な仕事を意味します:)
- 不必要な余分な作業を行う必要がある場合は、メンテナンスが面倒になる可能性があります。新しいプランを追加するたびに、マッピングコードに外部キー値をハードコーディングすることを覚えておく必要があります。
- データベースのエクスポート/インポート操作が不適切な場合、外部キーが変更される可能性があります。その場合、マッピングコードは機能しなくなり、ソフトウェアを再度デプロイする必要があります(または少なくとも変更された部分)。
提案された解決策
私がすることは、テーブルのtype
列に入れることです。その列には、特定の行からPlans
権利を構築する方法を知っているクラスの名前が保持されます。Subscription
しかし、なぜ各タイプを構築するためのオブジェクトが必要なのSubscription
ですか?各タイプのオブジェクトを構築するためにさまざまなテーブル(subscription_event
および)からの情報を使用するため、その動作を分離してカプセル化することは常に良いことです。complimentary_subscription
Plans
テーブルがどのように見えるか見てみましょう:
-計画表-
Id | 名前| タイプ| その他の列...
1 | 毎月| MonthlySubscriptionMapper
|
2 | 無料| ComplimentarySubscriptionMapper
|
それぞれがデータベースから行を取得し、サブクラスの適切なインスタンスを提供するSubscriptionMapper
メソッドを定義できます(または例では)。Subscription MapFrom(Row aRow)
Subscription
MonthlySubscription
ComplimentarySubscription
最後に、列で指定されたマッパーのインスタンスを取得するにはtype
(厄介なステートメントを使用せずにif
)case
、列の値からクラス名を取得し、リフレクションを使用して、そのクラスのインスタンスを作成できます。