[Seaside] Prevayler

Cees de Groot cg@cdegroot.com
Mon, 18 Mar 2002 09:15:50 +0100


[long. Avi, if you're short in time, at least read the paragraph enclosed in 
triple-plus '+++']

> Even the objects that 
> essentially do know about the persistence framework, your controllers, 
> don't know much, they just know that they use Command objects when they 
> need to edit model objects.
>
Duh. I guess this is a work-around for Java's lack of a meta object protocol. 
In Smalltalk, if you'd want to, you could do the same without using Command 
objects (just hack around in the class upon installation, and mark the class 
for a modified class browser so that these modifications aren't shown to the 
developer - now all method sends go through some of your code and you can 
intercept these to see whether any instance variables are touched and thereby 
register your object with the transaction). In Smalltalk, if you're doing the 
MVC thingy anyway, a Model should send events when it changes state so there's 
a natural hook for a persistence engine to listen in to what happens to 
persistent objects. However, see below for my ideas of 'transparence' here.
 
> Commands may seem like a burden (and to some degree they are), but it 
> seems to me that they may end up being easier to deal with than 
> transactions. 

Deep down below, there are transactions in every persistence system (if there 
aren't, the system isn't worth looking at - heck, even my Java replacement for 
gdbm (jdbm) sports transactions). The only choice you have is how far you go 
about hiding them.

> With WebObjects, I find that transactions get lost - I 
> find myself unable to call saveChanges() because I may be in the middle 
> of a larger transaction, and I don't want to persist it's data to the 
> database. 

Nested transactions or checkpoints to the rescue? Don't blame a concept 
because some product only has a partial implementation...

(question: why do you want to call saveChanges() in the middle of a 
transaction? What do you want to save? I only have this issue when using 
number sequence generators, that you typically want to commit whether the 
outer transaction succeeds or not; there are lots of solutions, but the 
simplest one is to just start another transaction in the current - if 
WebObjects can't do that, it's crippled).

> And of course it's a very new idea - it may be possible to combine it 
> with something like Aspect Oriented Programming to make it a truly 
> transparent framework.
> 
Not to be harsh on you :-), but the buzzword 'truly transparent framework' triggers alarms with me. Everything that claimed this was a dismal failure. I'm probably the only one, but usually after working for three days with the 'truly transparent framework' I find something that cannot be solved by the framework, and because it was all done so transparently it usually means it cannot be solved at all. 

+++ The idea, by the way, is not new. If you look at Squeak Webserver (some .at URL, forgot which one), it logs all calls to the webserver as a transaction log, and does a snapshot by doing an image save every day (now, *this* would be cool with Seaside - Avi, why didn't I think of this before? That could be a matter of hours to hook this in, shouldn't it? You probably could rip the necessary code from Squeak Webserver). In effect, the web requests are used as Command objects. When you crash the image, it'll replay all the web requests in order to restore state. +++

Anyway, on the topic of transparent persistence: it's a silver bullet category word (for more on silver bullets, read "The Mythical Man-Month" by Brooks. It's a must read). That is because persistence is a hard problem, with very broad scope, and therefore *whatever you do* you can only solve so much of the problem space by 'transparent persistence'. The closest that you can get is the Smalltalk object memory, now look at all the ugly hooks in classes that need to know when they are saved and when they are restored (every class having to do with network sockets, for starters). 

The issue with most frameworks is that they solve the problem that they think is *the problem* about persistence (in your case, they start with the MVC system), and most of them forget about the times when that problem is not your problem. Usually, it's solvable, but only by wading through lots of undocumented code - and of course, under Java, you should be happy if this code is accessible at all. 

Therefore, the best persistence engines I've used have a clearly defined low-level mechanism - you register objects manually, you start and commit transactions manually, you know how to get to the database root, you handle indexes manually, etcetera. On top of this, it *may* provide a 'transparent' layer that automatically registers changed objects; on top of this, it *may* cooperate with e.g. a specific web environment to provide for automated transaction handling. But in all cases, it should be clearly documented how the interactions with the lower layers are, and you should be able to write your own version of the higher layer if the persistence engine's designers happen to have made choices that don't fit your problem space.

As how this higher layer should look (IMHO, YMMV, ...): well, at least it should be higher than Model. It should be something like MetaDataPoweredModel - I have written an implementation of a fully (Jini) distributed application server, where the business objects where a mixture of XML-based metadata definitions and Java business methods - all the glue code (smart client stubs; server stubs; proxy cache stubs; O/R mapping code; Jini glue; syntactic validation methods; GUI hooks) was generated from these two files, and this made the whole business model abstract enough to be able to move it to completely different environments by just re-writing a couple of JPython code-generation scripts.

I've come to the conlusion that the Smalltalk 'Object'/'Class' implementations are simply too low-level to support complex business objects, and you need to pull it to higher levels anyway by adding metadata, using type patterns, etcetera - the biggest challenge here is to wrap it all in a class browser that is still as accessible as 'normal' Smalltalk code (presumably with a toggle methods/attributes). In my coding, I'm aiming at the Connectiva design, which I think is quite complete and generic (Papers on the Connectiva project can probably be found by entering 'Smalltalk Connectiva' in Google). From a rich class description, the whole Model/Command (and simple/default View/Controller) stuff can be inferred and therefore generated or metaobjectwrapped (for want of a better name :-)).

My conclusion - with the caveat that my remarks of the product are solely based on your description of it - would be that the layer that Prevayler aims at is too high to be generally practical, and too low to be really powerful. And design-wise quite possibly horribly crippled because they had to make it work in Java...



-- 
Cees de Groot               http://www.cdegroot.com     <cg@cdegroot.com>
GnuPG 1024D/E0989E8B 0016 F679 F38D 5946 4ECD  1986 F303 937F E098 9E8B