3

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

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

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

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

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

Hi @eoinoc. Что значит для пользователя иметь подписку «бесплатный доступ»? Какие атрибуты имеют общие с регулярными подписками и какие атрибуты имеют важное значение для «бесплатного доступа»? – nick2083

+0

@ nick2083 Это означает, что мы можем предоставить им доступ к системе на уровне членов, но им не нужно платить, в отличие от обычного члена, который должен платить ежемесячно по подписке. – eoinoc

ответ

5

Вопрос Обзор

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

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

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

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

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

Итак, чтобы отобразить этот пример модели в схеме базы данных, вы могли бы пойти с Class Table Inheritance узором, описанным в книге 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 строки.

Вы можете посмотреть на картины в «Узоры архитектуры предприятия Application» книги для дальнейшей помощи в том, как определить type значения столбцов, как использовать объект сопоставителя сделать Subscription экземпляра и так далее.


01/03/2012 Обновление: Альтернативы для определения и обработки type колонки

Вот обновление прояснить следующий вопрос, отправленный @enoinoc в комментариях:

В частности, для предлагаемого столбца type это может быть внешний ключ, указывающий на таблицу Plans, которая описывает различные типы подписки, например, сколько месяцев назад они истекают без оплаты. Это звучит логично?

Это нормально, чтобы иметь такую ​​информацию в таблице Plans, поскольку это не статическая информация, которую не нужно редактировать. Если это так, не делайте чрезмерного обобщения своего решения и помещайте это знание в соответствующий подкласс Subscription.

О внешнем ключе подхода, я могу думать о некоторых недостатках, идущих таким образом:

  • Помните, что ваша цель состоит в том, чтобы узнать, Wich подкласс Subscription использовать для каждой строки в таблице Plans. Если все, что у вас есть, это значение внешнего ключа (скажем, целое число), вам нужно будет написать код для сопоставления этого значения с классом, который будет использоваться. Это означает, что вам нужна дополнительная работа :)
  • Если вам нужно делать ненужную дополнительную работу, вполне вероятно, что основное внимание будет иметь боль: каждый раз, когда вы добавляете новый план, вам нужно будет запомнить hardcoding, это значение внешнего ключа в код отображения.
  • Внешние ключи могут измениться в случае неправильных операций экспорта/импорта базы данных. Если это произойдет, ваш код сопоставления больше не будет работать, и вам придется снова развернуть свое программное обеспечение (или, по крайней мере, измененную часть).

Предлагаемое решение

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

+0

Спасибо за ваши комментарии, так как он помогает мне понять фундаментальные дизайнерские решения, лежащие в основе предлагаемого решения. В частности, для предлагаемого столбца типа, это может быть внешний ключ, указывающий на таблицу «Планы», которая описывает различные типы подписки, например, сколько месяцев до истечения срока их действия без оплаты. Это звучит логично? Надеюсь, это не показало полного незнания того, что вы описали для наследования таблицы классов. – eoinoc

+0

Привет @eoinoc! Совсем нет, это хороший вопрос, и я очень рад помочь. Я обновил сообщение, чтобы ответить на этот вопрос, проверьте его. – nick2083

1

Абонентская подписка является подпиской, тем не менее. Его сумма платежа и цена за подписку будут равны нулю. Его срок действия будет таким, какой вы установили в качестве даты истечения срока действия бесплатной подписки или обычной подписки.

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

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

Смежные вопросы