Bamboo.
Prevalence, a .NET object prevalence engine.
http://bbooprevalence.sourceforge.net
Language: C#
One thing I would like to point out is the introduction of query objects: entities that model queries to the system's state. Query objects are very similar to command objects with the following notable differences:
# query objects don't change the state of the system
# and because of 1) - query objects don't need to be serializable (there's no need to write them to the command log)
Since the engine knows that query objects will not change the state of the system during execution it can use a more performant locking strategy - the reader/writer lock idiom - thus allowing multiple query objects to execute in paralel. The process is really simple and goes like this:
* before a query object is allowed to execute the engine must acquire a reader lock on the system
* before a command object is allowed to execute the engine must acquire a writer lock on the system
Thoughts?
Rodrigo B. de Oliveira
It is cool to have other people experiment with different prevalence styles. Diversity is life. --
KlausWuestefeld
Does the writer lock wait for all reader locks to be returned, then holds them until done writing? I'd like to see that in the Java version.
JonathanCarlson
That's exactly how it works.
RodrigoOliveira
I thought you would like to know that the latest release of
Bamboo.
Prevalence includes a totally transparent prevalence engine. What
I mean by that is: you don't need to write command and/or query objects anymore if you don't want to. The engine can intercept the calls to your system class and automagically transform them in command/query calls. Of course, there's a
performance penalty here but I didn't measure it just yet.
Follows the code for the AddingSystem class with transparent prevalence:
[Serializable]
public AddingSystem : System.MarshalByRefObject
{
private int _total;
public int Total
{
get
{
return _total;
}
}
public void Add(int amount)
{
_total += amount;
}
}
Yep, that's pretty much it. Property accessor are treated as if being query
objects and public method calls are treated as commands (you can use
attributes to change the default behavior though). The secret lies in the
client code:
PrevalenceEngine engine =
PrevalenceActivator.CreateTransparentEngine(typeof(AddingSystem), "data");
AddingSystem system = (AddingSystem)engine.
PrevalentSystem;
system.Add(10);
Console.WriteLine(system.Total);
A similar feature could be added to prevayler by using java.lang.reflect.Proxy.
RodrigoOliveira
I've implemented exactly this thing as a sample in Nanning. I'm using it successfully at two of my projects, it's really the most transparent and performant persistence solution I've seen.
JonTirsen
I presume the wrapping is done recursively, not only at the first level. How do you guys deal with object identities? If I query for the same object twice, will I get the same wrapper or will I get two different wrappers containing the same delegate? --
KlausWuestefeld
In
Bamboo.
Prevalence only the first level, the
PrevalentSystem itself, is wrapped by a proxy. This is intentional and it's similar to the design pattern Remote Facade (document by Martin Fowler somewhere). It's just a way to save some typing on creating all those command and query objects. --
RodrigoOliveira
How do commands find the objects they operate on? Is every business object given an ID? --
KlausWuestefeld
Every business object is given an ID at instantiation. (In
Nanning you can intercept/advise an object-instantiation.) The
PrevalentSystem must implement IdentifyingSystem (extends
PrevalentSystem) which has methods like: registerOID, getObjectWithID and getIDForObject. The object must either be advisable according to
Nanning (all public methods in an interface and instantiated via factory) or must extend a base-class. Only objects that is used as arguments to commands needs to be identifiable. Which methods are supposed to be
Prevayler-commands are indicated so with runtime attributes (also a feature of
Nanning).
--
JonTirsen
For the systems I've written, yes, a GUID (System.Guid) is given to each top level object. --
RodrigoOliveira
What is a "top level" object? What is a GUID? Is that ID transaparently given to the object? Does its class have to extend a specific class? --
KlausWuestefeld
Hmmm... Maybe top level object is not a good description, sorry. What I mean by top level object is: any object that the application might need to find directly. Maybe some code will make clearer:
class Enquete
{
private System.Guid _id;
private string _text;
private ArrayList _choices;
public Enquete(string text)
{
_id = System.Guid.NewGuid(); // this wil create a new UUID
_text = text;
}
public System.Guid ID
{
get { return _id; }
}
pubic string Text
{
get { return _text; }
}
public EnqueteChoice[] Choices
{
get { return (EnqueteChoice[])_choices.ToArray(typeof(EnqueteChoice)); }
}
public void AddChoice(EnqueteChoice choice)
{
_choices.Add(choice);
}
public void Vote(int choice)
{
((EnqueteChoice)_choices[choice]).Vote();
}
}
class EnqueteChoice
{
private string _text;
private int _votes;
public EnqueteChoice(string text)
{
_text = text;
_votes = 0;
}
public string Text
{
get { return _text; }
}
public int Votes
{
get { return _votes; }
}
public void Vote()
{
++_votes;
}
}
Enquete objects are the ones the UI operate on... So they need a ID. EnqueteChoice objects are dependent objects, they don't need an ID.
The prevalent system would include operations such as:
public void AddEnquete(Enquete enquete);
public Enquete GetEnquete(System.Guid enqueteID);
pubic void Vote(System.Guid enqueteID, int choice);
Does it make it clear? Thoughts?