Thursday, September 30, 2010

Value Objects - some notes

There is a controversy - or maybe a fuzzy consensus about what you should be able to do in templates. Some template engines, like Template Tookit, let you call subroutines and methods on stuff passed to the template. This is very useful - you don't need to care about turning the data into a specially formed hash that can be iterated by the template, you just pass the data there and can be sure that it can display it. But it can also be abused. I think everyone would agree that template should not for example change the data in the database, but where is exactly the limit is not clear. Value Objects seem to be a good answer, templates should work only with Value Objects.

The question is how sharply you can divide your objects into Value Objects and Service Objects. In ActiveRecord is hard to test Hevery proposes a design where the record in Active Record is a Value Object (newable in his terminology is a synonym) with no link to the repository object which would do all the database manipulation. This would work for normal attributes - you'd assign the value there on object creation - but what about relations? $user->books is very convenient, especially in templates, and it can be pure - if you know that you need the books on the user you could put them there when creating the user object. But then you'd have two kinds of objects - those with books, needed in some places - and those without, when you want to spare the additional database calls. Maybe we need subclasses of User? But then we'd need subclasses for all possible combinations of relations (users with books, users with friends, users with books and friends, ...), not to mention the mess with relations on the related objects.

3 comments:

Anonymous said...

DBIx::Class would have the same kind of User object in either case, and its "books" would be a lazy attribute of sorts (in Moose terms, which DBIC doesn't actually use). Ordinarily, using the ->books relation would access the database to fetch a bunch of rows, but if you specified { prefetch => books } when you retrieved the user, then the data would be "pre-loaded" when the first query was made, and the related books would be accessible (in the exact same way) without a new query.

It's a pretty good solution -- clever, but not too hard to understand. :)

zby said...

Sure it is a handy solution - but then User cannot be a Value Object - because it needs to know how to retrieve the related records. Should it be? Is it useful to divide objects into Value and Service Objects?

Stevan Little said...

In a way, this is how KiokuDB works. If you pass your Kioku objects into your template, you can change the objects all you want and nothing will be persisted. Only when you actually take these objects and explicitly save them in the DB does it persist. This is also a large reason why I do not like the automagical persistence found in most all ORMs out there. Put simply, often times with ORMs it is just too easy for someone to make a change to the DB, it should be a little more difficult/tedious to actually alter the DB (IMO at least).