【问题标题】:Subscription management logic订阅管理逻辑
【发布时间】:2012-01-26 21:41:31
【问题描述】:

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

我目前的做法:

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

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

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

【问题讨论】:

  • 嗨@eoinoc。用户拥有“免费访问”订阅意味着什么?哪些属性与常规订阅有共同之处,哪些属性对于“免费访问”是必不可少的?
  • @nick2083 这意味着我们可以授予他们会员级别的系统访问权限,但他们不必付费,不像普通会员必须按月付费。

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


【解决方案1】:

问题回顾

正如@leanne 所说,您正在为Subscription 建模,其专长是MonthlySubscriptionComplimentarySubscription(为这个答案命名)。

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

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

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

处理数据库中的继承

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

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

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

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

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

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

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

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

  • 或者在他们的用户行中为is_complimentarycomplimentary_expires_date 等列添加另一列?

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

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

如果您这样做,则每次subscription_event 更改时都必须处理数据同步(如果是按月订阅)。一般来说,我会尽量避免这种数据重复的情况。

示例解决方案

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

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

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

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


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

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

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

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

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

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

建议的解决方案

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

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

让我们看看Plans 表的样子:

-- 计划表--

标识 |姓名 |类型 |其他栏目...

1 |月刊 | MonthlySubscriptionMapper|

2 |免费 | ComplimentarySubscriptionMapper|

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

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

【讨论】:

  • 感谢您的 cmets,因为它帮助我了解建议的示例解决方案背后的基本设计决策。特别是对于建议的type 列,这可能是指向Plans 表的外键,该表描述了不同类型的订阅,例如在没有付款的情况下还有多少个月到期。这听起来合乎逻辑吗?我希望这不会显示您对类表继承所描述的完全无知。
  • 嗨@eoinoc!一点也不,这是一个好问题,我很乐意提供帮助。我已经更新了我的帖子来回答这个问题,请查看。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 2018-01-17
    • 1970-01-01
    • 1970-01-01
    • 2019-06-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多