eoi*_*noc 3 design-patterns user-management subscription e-commerce recurring-billing
对于用户可以是member或的系统admin,具有该member角色的用户必须使用定期订阅付费,或者获得免费访问权限.
我目前的做法:
user数据库表.subscription表包括针对用户的记录,如果他们有一个订阅.subscription_event表记录每一个计费或失败的付款.我可以查询这个以查看最后一个事件是否确实是一个成功的付款.但是,如果用户获得"免费"访问,我应该如何记录?
complimentary_subscription用户ID作为外键的表吗?subscription?is_complimentary和complimentary_expires_date?expires在用户行中添加更通用的列?问题回顾
正如@leanne所说,你正在建模一个人Subscription的专业化,比如说,MonthlySubscription并ComplimentarySubscription给他们一个这个答案的名字.
您知道订阅可能会过期:
MonthlySubscription,当用户没有支付当前月份的订阅时会发生这种情况ComplimentarySubscription,到期日期在分配给用户时分配正如你所看到的ExpirationDate,任何一个都是必不可少的属性Subscription,但你存储它的方式在每种情况下都是不同的.如果是第一种情况,你必须根据最后一个事件计算它,在后者中你可以直接检索它.
处理数据库中的继承
因此,要将此示例模型映射到数据库模式,您可以使用Martin Fowler的"企业应用程序架构模式"一书中描述的类表继承模式.这是它的意图:
"表示类的继承层次结构,每个类有一个表".
基本上,您将拥有一个包含类之间共享属性的表,并且您将在单独的表中存储特定于每个类的属性.
记住这一点,让我们回顾一下您提出的选项:
- 有另一个
complimentary_subscription用户ID作为外键的表吗?
有一个单独的表来存储ComplimentarySubscription特定的细节听起来不错,但如果你没有把这个与subscription表格联系起来,你最终会得到一个既有a MonthlySubscription又有a 的用户ComplimentarySubscription.它的外键应指向subscription表,该表告诉您用户是否有订阅(并且您必须为每个用户强制执行一个订阅).
- 为他们录制一个特殊的"订阅"
subscription?
是的,您必须记录用户每月订阅或免费订阅.但是,如果您正在考虑记录数量为零的特殊订阅,那么您正在寻找与您当前设计相匹配的解决方案,而不是为其搜索正确的模型(它可能也可能不是).
- 或者在列的用户行中添加另一列,例如
is_complimentary和complimentary_expires_date?
我个人不喜欢这个,因为你把信息放在它不属于的地方.考虑到这一点,您将在哪里存储免费订阅的到期日期(请记住,对于您正在计算它的月度,并且不存储到期日期)?似乎所有这些信息都在为自己的"家"而哭泣.此外,如果稍后您需要添加新类型的订阅,该表将开始混乱.
expires在用户行中添加更通用的列?
如果这样做,每次subscription_event更改时都必须处理数据同步(在每月订阅的情况下).通常我会尽量避免这种数据重复的情况.
样品溶液
在添加新类型的订阅时,我要做的是支持可扩展性,即让subscription表格在MonthlySubscripton和之间存储共享详细信息ComplimentarySubscription,添加一个type列密钥,使您可以区分与行相关的订阅类型.
然后,在其自己的表中存储特定于每个订阅类型的详细信息,引用父subscription行.
对于检索数据,您需要一个负责实例化Subscription给定行的type列值的正确类型的对象subscription.
您可以查看"模式企业应用程序体系结构"一书中的模式,以获得有关如何定义type列值,如何使用映射器对象进行Subscription实例化等方面的进一步帮助.
01/03/2012更新:定义和处理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语句),您可以从列的值中获取类名,并使用反射创建该类的实例.