Friday, March 27, 2009

Lifted... and let down

Alternative languages on the JVM are very 'in' at the moment, Scala in particular. Almost inevitably, there's a web framework for Scala - Lift. I looked at it briefly a while ago when embarking on a new web application. The 'sales pitch' was compelling, pulling together the best aspects of other frameworks whilst exploiting the power, scalability and safety provided by Scala. However, it was very early in its development with little documentation and few plugins. I decided in the end to use Grails.

Lift has matured somewhat since then so I took another look. I'm still very much taken by the pitch; the framework has matured significantly and the documentation has improved beyond all recognition (i.e. there is now some - including an incomplete but nevertheless good online book. There are also tutorials starting to pop up, so I thought I'd take another look.

Setup is easy, making extensive use of Maven. Next up is creating domain classes - and I'm afraid the first disappointment. Per all web frameworks, Lift comes complete with its own Object Relational Mapping capabilty - named, appropriately, Mapper. The tutorial uses a "ToDo" class as an example, with each item having the following properties: a "done" flag (boolean), a priority value (Integer), a description (String) and a relationship to the Owner of this particular item.

Very simple. In Grails, I'd code this as:

class ToDo {
Boolean done
Integer priority
String description
User owner
}

Pretty intuitive, intentional and economic.

Here's the Lift equivalent from the Tutorial:

class ToDo extends LongKeyedMapper[ToDo] with IdPK {
def getSingleton = ToDo

object done extends MappedBoolean(this)
object owner extends MappedLongForeignKey(this, User)
object priority extends MappedInt(this) {
override def defaultValue = 5
}
object desc extends MappedPoliteString(this, 128)
}

object ToDo extends ToDo with LongKeyedMetaMapper[ToDo]

Wow! Now granted, this is my first introduction to Scala and Lift. But there seems to be an awful lot going on there:
  • a class and an object with the same name;
  • the main ToDo class extends a parameterised type - which is parameterised with ToDo itself
  • a singleton ToDo which extends the ToDo class with another type parameterised with ToDo (the object or class? Not sure...)
  • a slew of names evidently drawn from the world of relational databases (PK, ForeignKey, MappedXX types).
Lets deal with the last point first. It's a philosophical discussion over whether the domain classes should provide a "clean" Object view of the concepts and hide the Relational mapping - or expose the Relational concepts explicitly. Grails takes the former approach, Rails the latter. Django is somewhere in between. My personal preference is the former - but its personal preference, so let's put that aside.

But even allowing for that, why does it have to be so complicated? From my perspective as a Scala and Lift newbie, it almost looks like a case of "we have all this cool stuff at our disposal, lets use it". Hence objects, classes, extension, traits and generics all make it in there.

Now I'm all for using the power of the language. But surely it should be used to both make things more intuitive and improve economy of expression? That certainly doesn't seem to be the case above.

Concept vs. Representation

Scala has some powerful XML handling capabilities, and Lift exploits these to enable tailoring of the way in which elements will be presented. Here's a snippet from one of the tutorials:

override def _toForm: Can[NodeSeq] = {
val funcName = S.mapFunc({s: List[String] => this.setFromAny(s)})
Full(</span><textarea style="font-family: courier new;" name="{funcName}" rows="{textareaRows.toString}" cols="{textareaCols.toString}" id="{fieldId}">{is.toString}</textarea><span style="font-family:courier new;">)
}


Powerful indeed. But wise? First off, it's mixing representation information with the domain class. Or, in MVC terms, mixing part of the View in with the Model. MVC says they should be separate, based on the principles of low coupling and high cohesion. It also complicates the workflow if a graphic designer is doing the UI / layout and a programmer doing the logic. As above, it seems like another case of wilful rather than judicious use of power.

And I'm afraid that's as far as I got. Maybe I should have persevered; Scala's Actor support holds significant promise for efficient scaling. But for now I'm not sold. Grails (and Groovy) may not have some of Scala's power features but the 'user interface' is significantly cleaner. So I'll continue grooving for now.

Tuesday, March 24, 2009

Relationships are more fun - part II

Trygve Reenskaug and James Coplien have just posted an article questioning the fundamentals of Object Orientation and the Model-View-Controller pattern.

It's interesting (if perhaps a little more wordy than necessary). And I don't agree with all of it (more of that later). But their characterisation of the relationship between the User's mental model and the Object model represented in software is spot on. But perhaps more interesting is their questioning of one of OO's central tenets: that all behaviour belongs in Objects (or Classes in programme language terms).

They assert that, whilst some behaviour does belong within the Object, much doesn't. To use their example: responsibility for increasing or decreasing an Account's balance properly lies with the Account. External parties can ask the Account to perform those operations, but it's up to the Account to do it. That's fully in line with the principles of Abstract Data Types on which OO is founded.

But what about performing a Transfer of funds from one Account to another? Where does that belong? Reenskaug and Coplien assert this doesn't belong with the Account Object - I agree. But that's how it would typically be handled in today's OO languages; so the Account class would typically have a method named "transferTo()", used as follows:

Account src;
Account dest;
src.transferTo(dest, 20.00);


What's wrong with that? From a mental model perspective, it doesn't work. When I think about transferring funds, I don't think about the responsibility lying with either the source or destination account. Sure they're both involved, but neither is responsible.

So where does it belong? In OOP terms we have nowhere else to put it other than in a Class. Reenskaug and Coplien suggest the notion of Interactions as first class entities; however their model still portrays an asymetric pattern (i.e. the responsibility for executing the transfer still lies with either the source or destination objects).

I prefer to think about it lying outside of either. But that doesn't mean the OO model is broken. Maybe it's just the way we use it today. Coming back again to the code example above:

src.transferTo(dest);

Now what happens if we look at this from a relationship perspective? What's the nature of the relationship? It's pretty obvious there's some link between the source and destination accounts - even if it's only transient. Here's an initial view as a UML class diagram?

Both source and destination are instances of Account, so the relationship would have to be recursive on Account. OK. Naming the ends? Easy; one is source, the other destination. Cardinality? Hmm, that's a bit more difficult. A given transfer has exactly one source and one destination account; and any account can be the source and/or target for many transfers. But we can't represent that on our relationship:
  • 1:1 would only allow each account to be related to a single other: i.e. at most one transfer.
  • many:many would only allow each account to be related to every other; but for every pair there could only be one transfer. Again, not what we need.
Let's revisit naming the relationship ends. I used "source" and "destination" above, which are role-based names. As I've stated previously, I prefer verb-based names because they bring out the "why" of the relationship. So how would we name the relationship ends using verb phrases?

Each Account may be the transfer source of one or more other Accounts
Each Account may be the transfer destination of one or more other Accounts


Kinda works, but it looks clumsy; it smells bad. So the cardinality doesn't work and the relationship naming is ugly. What does that tell us? Maybe there's no relationship? Well that's not true. It may be transient but both accounts are undeniably involved in something - otherwise there's no transfer.

Transfer. Hmm. What if, rather than looking at transfer as a verb, we treat it as a noun? What would that mean? Let's rework the model:

Let's examine the relationships again:

Each Transfer must debit exactly one Account
Each Account may be debited in one or more Transfers
An Account need not be debited in any Transfers

Each Transfer must credit exactly one Account
Each Account may be credited in one or more Transfers
An Account need not be credited in any Transfers


That's much better. The cardinality and phrasing capture exactly what we're trying to express - so the relationships look OK. But what about that "Transfer" class? Is it a proper class? Well, what does it mean to be a "proper" class? It's definitely an abstraction of behaviour - the transfer behaviour. What's more, it's a well encapsulated abstraction of behaviour. We are not loading the Account class with the responsibility for executing transfers; it need only respond to "credit" and "debit" requests: appropriate since it maintains the account balance.

What about state? That's OK too. The transfer value again properly belongs with the Transfer. We could also, if required, capture details of the time and success/failure of each transaction. Finally, we could, if required, track the state of each Transfer as it executes ("debiting source account", "crediting destination account", "completed successfully", ...).

There are critics of this approach who disagree with the idea of reification - treating behaviour as things. Rather, objects should be tangible things: Accounts, Customers, Chairs, Telephones, whatever - the things that are things in the real world.

The trouble is that doing so causes the situation that leads to a.transferTo(b). In fact, most of the interesting behaviour doesn't happen in the tangible objects. Transfer is an example of an Interaction object; one that coordinates a protocol amongst other objects. Interaction objects are useful and interesting precisely because they enable clean abstraction of behaviour amongst a number of other objects.

So is the OO model broken? No, not really. We just need to learn to use it properly. Going back to Coplien and Reenskaug, maybe it would help to promote Interactions to first class status, and hence increase focus on collaborations. Key to that is learning to love relationships - they're where all the fun happens.

Relationships are more fun

A number of years ago, I was fortunate enough to work for Steve Mellor's company. I remember vividly the first time I met him; I was very nervous. He was reviewing a domain model that I'd built, and I was amazed at how quickly he found a couple of logical flaws. One thing he said during the review has stuck with me ever since: "the fun is always in the relationships". It struck me as strange at the time; as someone schooled in OO, I had come to view Objects as the things that mattered. Relationships? Just pointers between Objects, right?

Wrong. Very wrong. Experience has fully vindicated Steve's assertion that the interesting stuff really does lie in the relationships, not the objects. In fact, the key to understanding any domain always comes down to formalising the relationships among the domain concepts. So I'm constantly frustrated by models in which relationships are second class citizens, demoted to pointers between objects with perhaps a role name. Something like this:
Why? Because it says nothing about the semantics of the relationship. Why does a Customer have a list of Accounts? Moreover, it says little about the domain - and doesn't prompt discussion or questions about the relationship. Can a customer really have more than one Account? How many Customers is each Account related to? What does the relationship mean when viewed from the Account's perspective anyway?

Steve (and my colleague, close friend and boss at the time Alistair Blair) instilled in me the importance of naming relationships using verb phrases. I've found it extremely valuable, more recently adopting the style recommended in Simsion and Witt's excellent book on Data Modelling. So the model above becomes:
With very little extra effort this provides much more information. We now know the relationship represents "Account Ownership". It doesn't cover being an authorised signatory, or a benefactor, or anything else. Are those things necessary? They may be. The point is the model now prompts that question.

The model also makes assertions about the relationship. An Account must be owned by exactly one Customer. Is that right? What about joint Accounts? Again, the model prompts the question. And so on. We can also generate textual facts (or "business rules") directly and algorithmically from the model, according to the technique described by Simsion and Witt. So we'd get:

Each Person may own one or more Accounts

Each Account must be owned by just one Customer

Furthermore, it tells us something about the behaviour, or interaction between Customer and Account. On first glance it's pretty simple: create a new account, assign it to the owner. Job done. But what happens if the Customer goes away? In typical OO programming terms, that responsibility would lie with the customer:

foreach a in self.accounts:
a.delete()


But why should it belong with the Customer? The required behaviour arises because of the Relationship: it's not intrinsic to the Customer. Another case in point is transfer of ownership. Where does that belong? The Customer? The Account? No. It belongs in the relationship.

So the fun does really happen in the Relationships - thanks Steve and Alistair for enlightening me.