セールスと権限制御の分離
Web上のコンシューマサービスにおいて、会員の契約プランによって出来ることが異なるものを考える。
ゴールドプラン: 会員限定スペシャルコンテンツが見れる、ポイント倍率5倍
シルバープラン: ポイント倍率2倍
ブロンズプラン
契約は月更新サブスクリプションを想定する。
このような場合、まず考えるべきは、セールス・マーケティングの世界(プランや契約)と、どのサービスが利用できるかの世界(権限)を切り離すことである。プランは短期的な売り上げをあげるために、新プランや使える機能の細かな調整が入ることが想定される。その度にプログラム修正が入っては大変だし、トラブルの元なので、その会員がどの機能を使えるかの制御にはプランを使わないように設計する。
このマーケと権限制御の分離ができていれば、「ゴールドプランのサービスがお試しで使えます!」などのキャンペーンも、アプリケーションとしては会員の権限(会員ランク)だけを期間限定で変更するだけでよい。
code: (mermaid)
classDiagram
direction LR
class プラン {
<<Resource>>
プランID
}
class 権限 {
<<Resource>>
権限ID
}
class 契約 {
<<Event>>
契約ID
会員ID
プランID
契約日時
契約有効期限
}
class 有効契約 {
契約ID
会員ID
}
class 会員 {
<<Resource>>
会員ID
}
class 無料会員
class 有料会員
会員 <|-- 無料会員
会員 <|-- 有料会員
有料会員 -- 有効契約
有効契約 "1"--"*" 契約
契約 -- プラン
プラン "*"--"*" 権限
会員 -- 権限
無料会員から有料会員に切り替わる場合は、「契約」が作られて契約したプランに応じた権限が、会員と関連付けられる。会員と権限の関連の実装は、通常、機能を使うたびに検索するわけではなく、ログインセッションなどに持たせておくことが多いと思われるので、会員の1属性でも構わない。
code: (mermaid)
classDiagram
class 会員{
<<Tabel>>
会員ID
権限: Integer ARRAY
}
または、特例対応などで会員ごとに細かな権限の制御をしないのであれば、権限のセットとしてロールや会員ランクのエンティティを作っておくと良い。
code: (mermaid)
classDiagram
direction LR
class プラン {
<<Resource>>
プランID
会員ランクID
}
class 権限 {
}
class 会員ランク {
会員ランクID
}
class 会員 {
会員ID
会員ランクID
}
プラン "*"--"1" 会員ランク
会員ランク "*"--"*" 権限
会員 "*"--"1" 会員ランク
難しいのは、会員ごとの権限(会員ランク)が契約プランが切り替わるタイミングで、変わらなくてはならないことである。人の操作によるイベントは発生しないので、時間経過とともに状態の変わるリソースは、悩ましいトレードオフを持つ。 1. 権限の有効な期間を持つ
2. イベント駆動で権限をUPDATEする
会員数が多すぎてバッチ処理がとてもじゃないが追いつかないのでなければ、2の明示的にイベントを発生させた方が、運用やトラブルシュートは簡単だろう。
code: (mermaid)
classDiagram
class ランク変更予約 {
会員ID
変更会員ランクID
変更予定日
}
すなわち
1. 契約開始、プラン変更のタイミングで「ランク変更予約」に登録しておく
2. 日次バッチでランク 変更予定日 <= TODAY() のものを取り出し、会員のランクをUPDATEする(または無料会員→有料会員、有料会員 → 無料会員の変更処理をする)。
3. (必要ならば) ランク変更した事実を「ランク変更」イベントとして記録しておく
4. ランク変更したものはランク変更予約から削除する。