Friday, December 28, 2012

Design by composition and persistance

Hi,

I'm stuck on the best way of implementing design by composition in Django, and was wondering if anyone had any suggestions/pointers/past experiences.

Say you're designing an event management program. Each event has a name, occurs in a suburb, and has a start time and end time. Say you want to model an event as follows.

class Event:
  string eventName
  string description
  Suburb suburb
  TimeWindow eventTimeWindow

class Suburb:
  string suburbName
  string state
  string postcode

class TimeWindow:
  datetime startTime
  datetime endTime
  def getDuration(self) # in seconds


The system contains a definitive list of Suburbs that can be chosen; that is a Suburb object has reference semantics. Consequently it makes sense for Suburb to have its own table, and thus the Event has a foreign key to a Suburb object.

However, a 'TimeWindow' object has value semantics. Two TimeWindow objects with the same attributes are not the same object (arguable, but for illustration let's assume this). Thus although TimeWindow could have it's own table, it seems like overkill - you have the cost of a join, and a TimeWindow object will only ever have a reference to exactly one Event (thus there's no benefit in normalising this part of the design).

This is analagous, in the extreme case, to the 'eventName' attribute. 'eventName' has value semantics, that is two 'eventName's that have the same sequence of characters will still be two separate objects. So you wouldn't create a separate 'EventName' table and reference this from the 'Event' class.

So from the OO software design angle, the Event's TimeWindow object is it's own object (with it's own state & behaviour), but at the persistance layer, TimeWindow's attributes are lumped in with eventName (and description) into the same 'Event' table. Having a separate TimeWindow object is desirable as it follows the general rule that design by composition is desirable. We should have small, coherent, abstracted models that are well defined.

My question is, is it possible to acheive this in Django? One possible solution is to declare TimeWindow as abstract and have Event inherit it. But this doesn't accurately model the 'has a' relationship (and seems like an abuse of Abstract inheritance) - Event would inherit the 'getDuration' method as well as TimeWindow's attributes, which more or less makes sense in this case but not in the general case.

So in your business logic you would want to use the object as: event.eventTimeWindow.getDuration() (and by using abstract inheritance this wouldn't be possible)

Another drawback is that you could only have one 'TimeWindow' object per Event. Let's say that we change the model slightly so that eventName and description are both EnglishString types:

class EnglishString:
  def isEnglish(self)
  def spellCheck(self)
  def titleCase(self) # capitalise the string as if for a header
  string contents

EnglishString is an object with value semantics - two instances with the same contents are not the same object. Thus you probably wouldn't want to store instances in their own table (to avoid join costs and overall complexity in how the objet is persisted). However, you can't use abstract inheritance (ignoring the previous concerns raised), as there are two instances contained with an Event instance.

Another option is to separate how the data is persisted from how it is used in the business logic. 

class Event:
  EnglishString eventName
  EnglishString description
  Suburb suburb
  TimeWindow eventTimeWindow

class PersistedEvent: 
  string eventName
  string description
  Suburb suburb
  datetime startTime
  datetime endTime

persistedEvent = PersistedEvent.objects.get(eventName = "John's Birthday")
event = Event(persistedEvent)
print event.suburb
print event.eventTimeWindow.getDuration()

But this feels:
 - difficult to get right
 - a large piece of work, that I'm sure someone has tried to solve before

And also any Django functionality that is bound to a Django model (eg: Forms, QuerySets) would need a separate conversion step.

 - as above, you'd query against the 'persisted' model and then use that to construct the domain model
 - also, you'd construct a form from the 'persisted' model
   - verification logic would have to sit on the 'persisted' model, as that's what extends Django's Model class
   - however, you'd probably want verification in the 'domain' model, leading to possible duplication
   - also verification for both models would probably have to be done slightly differently, as the 'persisted' model can 'see' all of the attributes from composed objects, whereas this isn't necessarily the case for the 'domain' model.

Is it even worth the effort?

Thanks

Taras

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To view this discussion on the web visit https://groups.google.com/d/msg/django-users/-/OwPJZWYqfTwJ.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to django-users+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

No comments:

Post a Comment