design-patterns - 订阅管理逻辑

标签 design-patterns e-commerce subscription user-management recurring-billing

对于用户可以是memberadmin的系统,具有member角色的用户必须通过定期订阅为它付费,或者被授予免费访问权限。

我目前的做法:


用户有一个user数据库表。
subscription表包含用户已订阅的记录。
subscription_event表记录每次开票或失败的付款。我可以查询一下,看看最后一个事件是否确实是成功的付款。


但是,我应该如何记录用户是否获得“免费”访问权限?


是否有另一个表complimentary_subscription以用户ID作为外键?
subscription中为他们记录一个特殊的“订阅”?
还是在用户行中为is_complimentarycomplimentary_expires_date之类的列添加另一列?
在用户行中添加更通用的expires列?

最佳答案

问题复习

正如@leanne所说,您正在建模一个Subscription,其专长是MonthlySubscriptionComplimentarySubscription(为它们给出此答​​案的名称)。

您知道订阅​​可以过期:


对于MonthlySubscription,这种情况发生在用户未支付当月订阅费用的情况下
对于ComplimentarySubscription,到期日期是在分配给用户时分配的


如您所见,ExpirationDate是任何Subscription的基本属性,但是每种情况下的存储方式都不同。如果是第一种情况,则必须根据最后一个事件进行计算,在后一种情况下,您可以直接检索它。

处理数据库中的继承

因此,要将这个样本模型映射到数据库模式,可以使用Martin Fowler的Class Table Inheritance书中描述的Patterns of Enterprise Application Architecture模式。这是它的意图:


“表示类的继承层次结构,每个类具有一个表”。


基本上,您将拥有一个表,这些表具有在类之间共享的属性,并且将特定于每个类的属性存储在单独的表中。

记住这一点,让我们回顾一下您建议的选项:



是否有另一个表complimentary_subscription以用户ID作为外键?



拥有一个单独的表来存储ComplimentarySubscription特定细节听起来不错,但是如果您不将此表与subscription表相关联,那么最终您可以同时拥有MonthlySubscriptionComplimentarySubscription的用户。它的外键应指向subscription表,该表可告诉您用户是否有订阅(并且您必须为每个用户强制执行一个订阅)。



subscription中为他们记录一个特殊的“订阅”?



是的,您必须记录用户每月或免费订阅。但是,如果您想记录一些特殊订阅(其数量为零),那么您正在寻找的是一种与您当前设计相匹配的解决方案,而不是为其寻找合适的模型(可能没有)。



还是在用户行中为is_complimentarycomplimentary_expires_date之类的列添加另一列?



我个人不喜欢这一信息,因为您将信息放置在不属于该信息的地方。考虑到这一点,您将在哪里存储免费订阅的到期日期(请记住,对于每月订阅而言,您正在计算它,而不是存储到期日期)?似乎所有这些信息都在为自己的“家”哭泣。
另外,如果以后需要添加一种新的订阅类型,该表将开始变得混乱。



在用户行中添加更通用的expires列?



如果这样做,每次subscription_event更改时都必须处理数据同步(对于按月订阅)。通常,我会尽量避免这种重复数据的情况。

样品溶液

添加新类型的订阅时,我倾向于扩展性的方法是使subscription表存储MonthlySubscriptonComplimentarySubscription之间的共享详细信息,并添加type列键,该键可让您区分哪种类型的预订与之相关。

然后,将特定于每种订阅类型的详细信息存储在自己的表中,并引用父subscription行。

为了检索数据,您需要一个对象来实例化给定Subscription行的type列值的subscription正确类型。

您可以查看“企业应用程序体系结构的模式”一书中的模式,以获取有关如何定义type列值,如何使用映射器对象进行Subscription实例化的更多帮助。



2012年1月3日更新:定义和处理type列的替代方法

这是一个更新,以澄清@enoinoc在评论中发布的以下问题:


专门针对建议的type列,这是否可能是指向Plans表的外键,该表描述了不同类型的订阅,例如在没有付款的情况下到期前有多少个月。这听起来合乎逻辑吗?


可以在Plans表中包含该信息,只要它不是不需要编辑的静态信息即可。如果是这种情况,请不要过度概括您的解决方案并将该知识放入相应的Subscription子类中。

关于外键方法,我可以想到这样的一些缺点:


请记住,您的目标是知道Subscription的最薄子类以用于Plans表中的每一行。如果您得到的只是一个外键值(例如,整数),则必须编写代码以将该值与要使用的类进行映射。这对您来说意味着额外的工作:)
如果您需要做不必要的额外工作,那么维护将很麻烦:每次添加新计划时,您都必须记住在映射代码中对它的外键值进行硬编码。
如果数据库的导出/导入操作不正确,外键可能会更改。如果发生这种情况,则您的映射代码将不再起作用,并且您将不得不再次部署软件(或者至少是已更改的部分)。


拟议的解决方案

我要做的是放在type表的Plans列中。该列将包含知道如何从特定行构建正确的Subscription的类的名称。

但是:为什么我们需要一个对象来构建每种类型的Subscription?因为您将使用来自不同表(subscription_eventcomplimentary_subscription)的信息来构建每种类型的对象,所以隔离并封装该行为始终是一件好事。

让我们看一下Plans表的外观:

-计划表-

ID |姓名|类型其他专栏...

1 |每月| MonthlySubscriptionMapper |

2 |免费| ComplimentarySubscriptionMapper |

每个SubscriptionMapper可以定义一个方法Subscription MapFrom(Row aRow),该方法从数据库中获取一行,并为您提供Subscription子类的正确实例(在示例中为MonthlySubscriptionComplimentarySubscription)。

最后,要获取在type列中指定的映射器的实例(不使用讨厌的ifcase语句),可以从列的值中获取类名,并通过使用反射来创建该实例的实例。类。

关于design-patterns - 订阅管理逻辑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8617585/

相关文章:

javascript - 谷歌分析电子商务跟踪安全吗?

android - 从 getBuyIntent 返回的 PendingIntent 始终为空

android - Android 应用中的应用内购买和订阅

design-patterns - 使用MVVM时,是否应该创建新的 View 模型以不同方式显示同一模型?

java - 在 REST API 中实现子资源

java - 设计模式 : Factory that creates one object

Windows Azure Portal 登录门户并收到错误 "We are having trouble logging you into the portal"

design-patterns - 交易脚本是反模式吗?

magento - 将 magento apis 用于电子商务网站

e-commerce - 电子商务付款 - 我如何向用户付款?