2

Let me introduce you into a minimalistic and made-up case with real-case chronological order, updates and problems.

User with ID only

I had a system, and it had User entity with ID column only. So, there was a class User and SQL table Users:

[Table("Users")] // database table
public class User
{
    [Key]
    [Column("Id")] // database column
    public long Id { get; set; } 
}

New properties

One day, a product owner comes to me and asks me to introduce 4 new properties:

  • birthday
  • age maturity (18 y.o. and above)
  • subscribtion end (paid until) date
  • is active, which means that subscription has not expired yet

Of course, at first I propose to not store "age maturity" and "is active", since it is a secondary evaluated value.

However, business analyst tells me: OK, do it your way, but we will need to see \ export table of all users with their maturity and activeness statuses, with up to 10000 users.
Now, I see 3 solutions and cannot decide which is better in terms of summarized assessment of architecture, performance and future supportability.

Possible solutions

1. Evaluable properties in domain model

[Table("Users")] // database table
public class User
{
    [Key]
    [Column("Id")] 
    public long Id { get; set; } 

    [Column("Birthday")] 
    public DateTime Birthday { get; set; }

    public bool IsMature 
    {
        get { return (DateTime.Now - Birthday).Years > SystemCfg.MaturityAge; }
    }

    [Column("PaidUntil")] 
    public DateTime? PaidUntil { get; set; }

    public bool IsActive
    {
        get { return PaidUntil.HasValue && PaidUntil.Value >= DateTime.Now; }
    }
}

Pros:

  • Actual values: values are always actual
  • Business-logic placement: business-logic is stored in a single place

Cons:

  • Performance: in real example, PaidUntil is actually a property which takes some time to be calculated, sometimes with web-requests, so calculating it for 10000 users dynamically can become dramatically slow in future

2. Database-stored properties, recalculated at sign in

[Table("Users")] // database table
public class User
{
    [Key]
    [Column("Id")] 
    public long Id { get; set; } 

    [Column("Birthday")] 
    public DateTime Birthday { get; set; }

    [Column("IsMature")] 
    public bool IsMature 

    [Column("PaidUntil")] 
    public DateTime? PaidUntil { get; set; }

    [Column("IsActive")]
    public bool IsActive { get; set; }
}

// on sign in:
user.IsMature = (DateTime.Now - user.Birthday).Years > SystemCfg.MaturityAge;
user.IsActive = PaidUntil.HasValue && PaidUntil.Value >= DateTime.Now;
_userRepository.Update(user);

if (!user.IsActive)
    return AuthResult.UserIsInactive;

Pros:

  • Performance: values are only recalculated on sign in, which brings the best performance among these options

Cons:

  • Business-logic placement: business-logic is stored in a single place, but model is now anemic
  • Actual values: values are mostly not actual for report / export views, since are updated on user sign in

3. Database-stored properties, recalculated on schedule

Same model, but data is updated by a special Windows Service, which runs through my database and keeps data up to date.

Pros:

  • Performance: values are calculated on a separate thread / application / server
  • Actual values: values are guaranteed to be actual UNTIL update server becomes overloaded or down

Cons:

  • Business-logic placement: business-logic is stored in a separate, non-domain application
  • This approach sounds weirds

So, what approach should I use for this special case?
What approaches do you and people usually use?

  • Can you export from a view, not the table proper? A view can have all manner of calculated fields. – 9000 May 4 '17 at 5:07
  • How will the report be created? Is it always going to go through your domain model, or are you expecting it to be created directly from database? Also, how strict are requirements on "stale" data? Does the timings have to be second-precise, or can it be few hours stale? And if you keep User in memory, should IsActive be updated if it changes? – Euphoric May 4 '17 at 5:52
  • This seems easy enough to setup an experiment and see first hand the processing cost difference. – whatsisname May 4 '17 at 7:06
  • @9000 It is possible for some properties, but impossible for others. It would also mean that some properties' calculation business-logic will be distributed between different layers - BL and Database. – Yeldar Kurmangaliyev May 5 '17 at 4:34
  • @whatsisname Yeah, I can say in advance that approach 2 and 3 are both very performant, but I am also curious which to prefer in terms of DDD, architecture and maintanability. – Yeldar Kurmangaliyev May 5 '17 at 4:35
2

Your first solution : calculate the domain model is the best.

In fact though you have two types of calculated fields here. Ones which change depending on the date on which you look at the object and ones which change depending on an system event (paidUntilDate)

Fields which change according to when you look at them have to be calculated on the fly or risk being out of date. The export will be incorrect the day after its generated and you should probably attempt to avoid such fields at all where possible.

However the PaidUntilDate should be calculated when the event which changes it happens. Presumably when the customer buys a subscription.

This gets around your long calculation problem. Perhaps you can just do a join to a subscriptions table instead of the calc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.