views:

151

answers:

2

Hi,

We are launching a website (paid subscription) and the sign up process includes entering an activation code. Activation codes are printed on scratch cards and sold via offline channels. Some of these cards are for 1 month access. Others are for 3 months and 1 year. Activation codes are unique 10-digit random numbers.

When the access expires, users can buy another activation card and extend the subscription by entering the new activation code. Additionally, we should also be able to extend their subscription if they request for it. For example, until a certain date (e.g. 1 additional week).

Considering the above information, how would you design the DB for the user-activation_code relationship? Do you think this design is good?

tbl_user
----------------
id
name
status_id

tbl_user_status
----------------
id
description

tbl_activation_code
----------------
activation_code
activation_code_type_id
activation_code_status_id
user_id
activated_date
expiry_date

tbl_activation_code_type
----------------
id
description

tbl_activation_code_status
----------------
id
description

Update: Activation codes will be required only:

1) Upon initial sign up

2) Closer to the access expiry date (say, 7 days) when the system displays a notification with a link to page to enter the activation code

3) After expiry, when a user tries to login, she will be asked for the activation code

Therefore, a user is not expected to key in the activation code as and when wanted.

+1  A: 

I would consider a bit of denormalisation - at the moment, to determine whether a user currently has access or not you have to look through potentially multiple records for that user in tbl_activation_code to see if there is an active record for that user.

So it might be worth adding a surrogate IDENTITY/autonumber field in tbl_activation_code, and adding a foreign key to that in tbl_user - this would point to the user's current activation code record, simplifying the scenarios where you need to find the current state of a user's access. This way, a user record will always reference directly their current activation code, plus you still have the full history of their previous codes.

AdaTheDev
thanks. any particular reason why I shouldn't use the activation_code field foreign key in tbl_user? why additional field?
Nuku
Just because using an identity field will be shorter - therefore requires less storage space, and should be more efficient to query/join on/index
AdaTheDev
so, this autonumber would be a FK in tbl_user and tbl_user.id would be FK in tbl_activation_code?
Nuku
I think the previous approach suggested by Mike would work better. He goes away with not looking up into the activation codes, each time you want to log user in. this is brilliant. You only need to change the user's access expiry status when he applies a new card, and then you can forget about that card.
husayt
@Nuku - Yes@husayt - yes, I agree to a point what you're saying and I did think about that originally. But the reason I didn't suggest that approach is for the extra value you get from linking to the "current" activation code for a use - you have easy access to the full details of a user's current activation code. With the alternative, you don't have this. Of course, this might not be needed.
AdaTheDev
+3  A: 

It's not bad. However, I would suggest that you add two fields to tbl_user:

tbl_user
----------------
id
name
status_id
activated_date
expiry_date

Of course, activated_date holds the date they were first activated, while expiry_date holds the date when they will expire. You also need a procedure to update this expiry_date, whenever they buy a new card. This procedure should handle two cards with overlapping dates, so the user doesn't double-up payment for a particular period. For example:

  • Card 1 - Sep 1 to Sep 30
  • Card 2 - Sep 16 to Oct 15

There are fifteen days of overlap there, so the user's activated_date should be Sep 1, while their expiry_date should be Oct 30 (Oct 15 + 15 days).

Considering this, I would change tbl_activation_code, as expiry_date becomes a bit misleading. Instead, create a column called access_days that will be used to calculate the user's expiry_date.

Also, if you want to remember cards that were issued, even if not activated, then I would split tbl_activation_code into two tables:

tbl_activation_code
----------------
activation_code
activation_code_type_id
activation_code_status_id
access_days

tbl_activation
----------------
activation_code_id
user_id
activated_date
Mike Hanson
tbl_user, of course, has a few other fields such as created_date, which is in fact is the activated_date you propose. as for the expiry_date in tbl_user, it looks as if we're "expiring" the user. probably not a good idea, huh?I feel the expiry_date should be for the activation_code, which would be calculated based on the activation_code_type_id and activated_date.
Nuku
Actually, Mike speaks wisdom. Expiry_date doesn't mean the user expires, you can rename it as you wish. But besides benefits Mike has listed, this gives you one place lookup to determine if the user should be allowed in or not. This is also a way to write a scalable solution. Otherwise you would have to search through all the cards to see if he has activated any, find their max date and so on.
husayt
As husayt says, you can rename the expiry_date field in tbl_user to activation_expiry_date. With regards to an expiry_date being associated with the card, it depends on how you view the cards. If they're considered to be "duration purchases", then expiry_date does not belong in tbl_activation_code. For example, what happens if the user buys and activates two 30-day cards on the same day. Do they get activated for 30 or 60 days? If, however, they "waste" their card by activating two in one day, then go ahead and leave the expire_date with the card (and anger your users <g>).
Mike Hanson