Логика управления подписками - PullRequest
3 голосов
/ 23 декабря 2011

В системе, где пользователь может быть member или admin, пользователи с ролью member должны либо заплатить за это с помощью периодической подписки, либо получить бесплатный доступ.

Мой текущий подход:

  • У пользователей есть таблица базы данных user.
  • Таблица subscription включает запись для пользователя, если у него есть подписка.
  • A subscription_event таблица фиксирует каждый платеж или несостоявшийся платеж. Я могу запросить это, чтобы узнать, был ли последний случай действительно успешным платежом.

Но как мне вести запись, если пользователю предоставляется «бесплатный» доступ?

  • Есть еще одна таблица complimentary_subscription с идентификатором пользователя в качестве внешнего ключа?
  • Запишите специальную «подписку» для них в subscription?
  • Или добавьте еще один столбец в пользовательскую строку для таких столбцов, как is_complimentary и complimentary_expires_date?
  • Добавить более общий столбец expires в строку пользователя?

Ответы [ 2 ]

6 голосов
/ 02 января 2012

Обзор вопросов

Как сказала @leanne, вы моделируете Subscription, чьи специализации, скажем, MonthlySubscription и ComplimentarySubscription (чтобы дать им имя дляэтот ответ).

Вы знаете, что срок действия подписки может истечь:

  • Для MonthlySubscription это происходит, когда пользователь не оплатил подписку текущего месяца
  • Для ComplimentarySubscription дата истечения назначается, когда она назначается пользователю

Как видите, ExpirationDate является существенным атрибутом любого Subscription, но способ, которым выХранить его по-разному в каждом конкретном случае.Если в первом случае вам придется рассчитать его на основе последнего события, во втором вы можете получить его напрямую.

Работа с наследованием в базе данных

Таким образом, чтобы сопоставить этот образец модели со схемой базы данных, вы можете использовать шаблон Наследование таблиц классов , описанный в книге Мартина Фаулера Patterns of Enterprise Application Architecture .Вот его намерение:

«Представляет иерархию наследования классов с одной таблицей для каждого класса».

В основном у вас будет таблица с атрибутами, совместно используемыми вобщие для классов, и вы будете хранить атрибуты, специфичные для каждого класса, в отдельной таблице.

Учитывая это, давайте рассмотрим предложенные вами варианты:

  • У вас есть другая таблица complimentary_subscription с идентификатором пользователя в качестве внешнего ключа?

Наличие отдельной таблицы для хранения ComplimentarySubscription конкретных деталей звучит хорошо, но если вы не связываете этоодин с таблицей subscription, вы можете получить пользователя, у которого есть MonthlySubscription и 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), вы можете взять имя класса из значения столбца и,с помощью отражения создайте экземпляр этого класса.

1 голос
/ 01 января 2012

Бесплатная подписка, тем не менее, является подпиской.Сумма платежа и стоимость подписки будут равны нулю.Дата истечения срока его действия будет соответствовать дате окончания срока действия бесплатных подписок по сравнению с обычными подписками.

Вам потребуется только отдельный столбец или таблица, если ваша текущая настройка не позволяет успешно запрашивать бесплатные подпискипользователи, основанные на этих факторах.

Примечание. Я предполагаю, что в данный момент вы связываете пользователя с ценой подписки.В любом случае вам нужно знать об этом, если в будущем цена подписки изменится или если вы захотите также включить скидки, скажем, для постоянных или рекламных пользователей.

...