4

对于用户可以是member或的系统admin,具有该member角色的用户必须通过定期订阅为其付费,或者获得免费访问权限。

我目前的做法:

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

但是,如果用户被授予“免费”访问权限,我应该如何记录?

  • 有另一个complimentary_subscription以用户 ID 作为外键的表吗?
  • 为他们记录一个特殊的“订阅” subscription
  • 或者将另一列添加到他们的用户行中以获取is_complimentarycomplimentary_expires_date
  • 向用户行添加更通用expires的列?
4

2 回答 2

8

问题回顾

正如@leanne 所说,您正在建模一个Subscription其专长是,比如说,MonthlySubscriptionComplimentarySubscription(给他们一个这个答案的名字)。

您知道订阅​​可能会过期:

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

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

处理数据库中的继承

因此,要将这个示例模型映射到数据库模式,您可以使用Martin Fowler 的企业应用程序架构模式一书中描述的类表继承模式。这是它的意图:

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

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

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

  • 有另一个complimentary_subscription以用户 ID 作为外键的表吗?

有一个单独的表来存储ComplimentarySubscription特定的细节听起来不错,但如果你不将这个表与subscription表关联起来,你最终可能会得到一个同时拥有 aMonthlySubscription和 a的用户ComplimentarySubscription。它的外键应该指向subscription表,该表告诉您用户是否有订阅(并且您必须为每个用户强制执行最多一个订阅)。

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

是的,您必须记录用户每月或免费订阅。但是,如果您正在考虑诸如记录金额为零的特殊订阅之类的事情,那么您正在寻找与您当前设计相匹配的解决方案,而不是为其寻找正确的模型(可能也可能不是)。

  • 或者将另一列添加到他们的用户行中以获取is_complimentarycomplimentary_expires_date

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

  • 向用户行添加更通用expires的列?

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

样品溶液

在添加新类型的订阅时,为了支持可扩展性,我会做的是让表存储和subscription之间的共享详细信息,添加一个列键,让您区分一行与哪种订阅相关。MonthlySubscriptonComplimentarySubscriptiontype

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

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

您可以查看“企业应用程序架构模式”一书中的模式,以获得有关如何定义type列值、如何使用映射器对象进行Subscription实例化等方面的进一步帮助。


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

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

特别是对于建议的type列,这可能是指向一个Plans表的外键,该表描述了不同类型的订阅,例如在它们到期前多少个月没有付款。这听起来合乎逻辑吗?

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

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

  • 请记住,您的目标是了解表Subscription中每一行要使用的子类Plans。如果你得到的只是一个外键值(比如一个整数),你必须编写代码将该值映射到要使用的类。这意味着你需要额外的工作:)
  • 如果您必须做不必要的额外工作,维护可能会很痛苦:每次添加新计划时,您都必须记住在映射代码中硬编码它的外键值。
  • 如果数据库导出/导入操作不当,外键可能会发生变化。如果发生这种情况,您的映射代码将不再工作,您将不得不再次部署您的软件(或者,至少,更改的部分)。

建议的解决方案

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

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

让我们看看Plans表格可能是什么样子:

-- 计划表 --

身份证 | 姓名 | 类型 | 其他栏目...

1 | 月刊 | MonthlySubscriptionMapper |

2 | 免费 | ComplimentarySubscriptionMapper|

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

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

于 2012-01-01T20:03:00.027 回答
1

尽管如此,免费订阅也是一种订阅。其到期付款金额和订阅价格将为零。它的到期日期将是您为免费订阅与常规订阅设置的到期日期。

如果您当前的设置不允许您根据这些因素成功查询免费订阅/用户,则您只需要一个单独的列或表。

注意:我在这里假设您当前将用户与订阅价格相关联。无论如何,您都需要知道这一点,以防将来订阅价格发生变化,或者如果您还想为经常性或促销用户启用折扣。

于 2011-12-31T20:19:21.500 回答