Best of both worlds: Csla with NHibernate

Best of both worlds: Csla with NHibernate

Old forum URL: forums.lhotka.net/forums/t/3638.aspx


Tolomaüs posted on Tuesday, October 02, 2007

I'm currently working on a project to combine Csla with NHibernate. Unlike the other projects that share the same goal that I read about, I started with NHibernate and tried to add Csla on top of it.

From the beginning, I have made some clear responsibilities:
-use NHibernate to do the persistence of the business objects into a relational database
-use Csla to handle the binding with the user controls, the validation, the security, and all other things that it supports but which I don't know yet at the moment

I would like to share my ideas with you and ask your advice on it. So please read on ...

Before continuing, I must admit that I am currently only investing two architectures: two-tier/rich client and  three-tier/SOA. So my solution will not be as generic in an architectural way as using pure Csla.

Another thing that I should mention is that I want my business objects as pure as possible in the sense that they should only be responsible for what they are designed: to contain business logic. Practically this means that I want to put as little persistence logic or presentation logic as possible inside them. This makes them lighter, more understandable and more portable - at least in theory.

So how did I do it?

First of all I modelled my business entities in a DDD way taking into account the restrictions that NHibernate requires: provide a default constructor and use an IList for child collections.

Then I subclassed them with a customized version of the BusinessBase, one of which I removed all the persistence logic code. I also modified the BusinessListBase to make it "wrappable" around the simple IList collections that NHibernate provides. (And on top of that, I have created my own subclass of the BusinessListBase to resolve some issues concerning in-line editing within a bound grid.) Using this approach avoids the need to synchronize the NHibernate-provided collection to the more powerful Csla-collection.

The code for retrieving a list of organisations and showing them in a grid will typically look like this:

            //Retrieve a list of organisations
            IList<Organisation> organisationList = unitOfWork.Repository.RetrieveAllOrganisations();

            //Wrap the simple IList into an IBindinglist
            organisationCollection = new EntityList<Organisation>(organisationList);

            //Register the method to the code that should be executed when the GUI wants to create or remove an object
            organisationCollection.AddingNew += new AddingNewEventHandler(organisationCollection_AddingNew);
            organisationCollection.AddedItem += new EventHandler<AddedItemEventArgs>(organisationCollection_AddedItem);  
            organisationCollection.RemovingItem += new EventHandler<RemovingItemEventArgs>(organisationCollection_RemovingItem);

            //Hand the IBindinglist over to the GUI
            organisationBindingSource.DataSource = new SortedBindingList<Organisation>(organisationCollection);//The SortdBindingList wraps around the actual list and makes it sortable


All this gives me a business object model that is pretty pure: only the mentioning of the BusinessBase base class refers to something else than real business logic. But I'm perfectly OK with this, because I consider this rule as a more theoretical one: I prefer it, as long as it doesn't hurt anything else.

Another advantage that I see in moving the persistence logic out of the objects, is that I can allow to have multiple different object graphs in one transaction (or unit-of-work as I call it in my code). NHibernate will take care of the exact sequence of insert/update/delete statements and of filling in any autonum foreign-key fields and so on.

Although Csla's multiple-level undo is a very interesting feature, I have not been able to combine it with NHibernate. As far as I can see, this is due to the fact that Csla falls back to the stacked object graph clone when undoing the changes but NHibernate still has a reference to the original objects at that moment. So therefore I'm thinking of providing my own "nested-transaction" mechanism, by first duplicating an object graph and later synchronising the duplicated object graph with the original one.

I have created an example project for this so if you're interested, feel free to take a look at it: http://www.paarden.be/NHibernate/Sol_PaardenConfigurator.zip

Also, feel free to give me your advice on my approach, I would really appreciate it. There are certainly a lot of scenarios that I haven't thought of yet and for which this solution might not work.

I think it would be a great help to a lot of developers to have a way to combine these two excellent technologies into one consistent framework!

Tolomaüs.






skaue replied on Thursday, October 04, 2007

Without having looked at the zip (its on my desktop and I will poke the code after writing this) your intentions are almost exactly the same as what I have dealt with the last weeks. I started off with just Nhibernate and tried to build csla on top of this. At the moment I am researching on how my business-objects (CSLA-based) will be and what they need to do.
Very interesting to read your concerns on the multilevel-undo not compatible with nHibernate (out-of-the-box). Would be interested in seeing how you solve this in the future [:-)]

Thank you for sharing your ideas and thoughts. This thread might grow if more people pick up on the same architecture. I found the NHibernateProjectTracker example not to useful since it didn't really seperate business from POCO/DTO/datalayer/table-mapping.

Tommy

Tolomaüs replied on Sunday, October 07, 2007

I have uploaded a new version of the project on my site (http://www.paarden.be/NHibernate/Sol_PaardenConfigurator.zip).

This version has support for the multiple-undo functionalities, although I have called it nested-unit-of-works.

The way I see it, there is conceptually no difference between a regular unit-of-work, which takes a local copy of data from the database and writes any modifications to it back to the database when committed, and such a nested-unit-of-work, which takes a copy of the in-memory objects and writes any modifications to them back to the original objects when committed.

The following example code should make it bit clearer:

Assume that I have an organisation that can organize events and I want to manipulate the organisation with id 4 in a nested-unit-of-work.

First of all I create my regular unit-of-work (this is a layer on top of the NHibernate session):

UnitOfWorkFactory unitOfWorkFactory = new UnitOfWorkFactory();

UnitOfWork unitOfWork = unitOfWorkFactory.OpenUnitOfWork(ConnectionMode.Disconnected);

 

Now I retrieve the organisation and initialize the associated events:

Organisation originalOrganisation = unitOfWork.Repository.RetrieveOrganisation(4);

unitOfWork.Initialize(originalOrganisation.Events);

 

Nothing special so far. But now I want to manipulate this organisation with its events but be able to fall back on the retrieved (unmodified) one in case I change my mind. Therefore I register the organisation in a nested-unit-of-work, which will provide me a duplicate of the organisation, a "working copy".

(Note: for simplicity I make abstraction of the UI-binding code.)

NestedUnitOfWork nestedUnitOfWork = unitOfWork.CreateNestedUnitOfWork();

Organisation organisationDuplicate = (Organisation)nestedUnitOfWork.Register(originalOrganisation);

 

Now I can make any modifications, like changing the organisation's primitive fields, adding or removing events, etc.

organisationDuplicate.Description = organisationDuplicate.Description + " MODIFIED";

organisationDuplicate.Events.Add(new Event());

 

When I'm done I commit, when I change my mind I rollback:

nestedUnitOfWork.Commit();

or

nestedUnitOfWork.Rollback();

 

There is also an example of these nested-unit-of-works in the project code. Choose "Manage organisations" on the main menu, and Press the Modify-button for an event day.

Let me know what you think of it,

Tolomaüs.

skaue replied on Monday, October 08, 2007

I opened the source of your example and started wondering how I would go around plug this into my current solution. Since I am kind of nubish on .Net I don't even know if its that pluggable to an existing architecture.

In our solution we even have to evalutate a logged in user and point the sessionfactory (NHibernate) to the users database/connectionstring. In other words, userA might use databaseA and userB might use databaseB. This means that I would have to spend some time figuring out how to expand your unitOfWork-thingy to dynamically use the correct sessionfactory.

DavidDilworth replied on Monday, October 08, 2007

I haven't looked at your code, but I'm curious and slightly suspicious that you feel you need to rewrite the standard CSLA n-level undo functionality, just because you are integrating with NHibernate.

I can't see why the n-level undo that CSLA provides out-of-the-box, would not meet your requirements.

If you are simply using NHibernate as the ORM to persist an object and it's graph, then why does n-level undo become an issue?  Surely it's a BO to UI issue, not a persistence one?

Are you sure you understand the implications of using CSLA and NHibernate when working with a Data Portal?  Are you holding NHibernate Sessions open against the DB?

Would your BOs work in a web (disconnected) environment? 

Clearly, if you are using NHibernate as your "main" technology then some of these questions will not matter to you.  But just be careful that you're not re-inventing the wheel for such a built-in piece of CSLA functionality.

skaue replied on Monday, October 08, 2007

One thing I notice from both CSLA+NHibernate examples, the CslaContrib and this Horses-example, is that they do not try so much to seperate the CSLA and NHibernate into seperate layers/projects. Wouldn't it be smart to try extract the NHibernate code into a seperate layer so it could be plugged out or exhanged with some other ORM (if needed). I'm not saying anyone would do this in a real product/architecture , but as for a sample code it would be clever since then people could swap the NHibernate layer with something else.

@DavidDilworth: are you saying that the n-level undo *will* work out of the box? Any experience on that?

DavidDilworth replied on Monday, October 08, 2007

If CSLA supported the concept of "Data Providers" then the ORM technology could be easily separated out.  I know Rocky once referred to this being in the pipeline, but I'm not sure if this is included in the latest release.  I'll have to go and have a look.

As for CSLA n-level undo.  If Rocky says the framework supports it, then I reckon it's a pretty sure bet that "it does what it says on the tin"!

We haven't used it in an application, but we did some basic tests with it some time back.  It worked.

Tolomaüs replied on Monday, October 08, 2007

Hello Tomy and David,

Thanks already for your comments so far. I wil try to answer as clearly as possible the remarks/questions you have made:

skaue:

In our solution we even have to evalutate a logged in user and point the sessionfactory (NHibernate) to the users database/connectionstring. In other words, userA might use databaseA and userB might use databaseB. This means that I would have to spend some time figuring out how to expand your unitOfWork-thingy to dynamically use the correct sessionfactory.


Do databaseA and databaseB have the same structure or are thy separate databases? In the first case you would need to adapt th UnitOfWorkFactory's constructor so you can pass a connectionstring or some other "database"-identifier. In the constructor you can then override the connectionstring that came from the hibernate.cfg.xml. In the second case you would have two different UnitOfWork's: one for each database.

Tolomaüs replied on Monday, October 08, 2007

skaue:
One thing I notice from both CSLA+NHibernate examples, the CslaContrib and this Horses-example, is that they do not try so much to seperate the CSLA and NHibernate into seperate layers/projects. Wouldn't it be smart to try extract the NHibernate code into a seperate layer so it could be plugged out or exhanged with some other ORM (if needed). I'm not saying anyone would do this in a real product/architecture , but as for a sample code it would be clever since then people could swap the NHibernate layer with something else.

@DavidDilworth: are you saying that the n-level undo *will* work out of the box? Any experience on that?


As far as abstracting NHibernate from the business layer, this would not take more than an hour of work. Create an interface for the unit-of-work and for the repository and provide an implementation for NHibernate. You could then provide an implementaion for any other ORM (as long as it provides its own unit-of-work equivalent of course).

Tolomaüs replied on Monday, October 08, 2007

David,

DavidDilworth:

I haven't looked at your code, but I'm curious and slightly suspicious that you feel you need to rewrite the standard CSLA n-level undo functionality, just because you are integrating with NHibernate.

...

Clearly, if you are using NHibernate as your "main" technology then some of these questions will not matter to you.  But just be careful that you're not re-inventing the wheel for such a built-in piece of CSLA functionality.



I understand your worries when seeing another attempt to combine Csla with NHibernate, but I think both solve quite a different set of architectural requirements.

Before I started my project I have clearly stated my own architectural requirements. Compared to the variety of architectures and technologies that Csla supports and to the possibility to switch almost on-the-fly between them, my own requirements in this area are rather simple: I only need to support a two-tier WinForm/WebForm and a three-tier SOA webservice architecture.

On the other hand, for the two-tier architecture, I need a very flexible way to manipulate whatever business objects the developer needs to implement his use case and to store the state when the use case ends. A lot of these functionalities are provided by the NHibernate session if you reuse it throughout the whole use case. It gives me caching and automatic dirty checking, it manages the autonums  (interesting when you try to add a parent with children and grandchildren!) and it intelligently orders the insert/update/delete statements of the different tables.

(Note that I use this session in a disconnected manner, only making a connection to the db when needed.)

Precisely because I keep the session throughout the life of the use case, it is not possible to use the out-of-the-box multiple-level undo functionality. (The session keeps a link to all busines objects so whenever Csla decides to fall back on its serialized instances NHibernate gets lost.) I'm sure multiple-level undo will work in a one-session-per-db-call manner, as in your solution.

So the overlap with Csla - and therefore the existing NHibernate integration project - is not really that big and that's the main reason why I decided to build something myself.

Thanks a lot for the advice! I really appreciate it.




Tolomaüs replied on Monday, October 08, 2007

FYI, there are the requirements I stated before I started (extract from the enclosed README file):

>> Requirements of the architecture:
- Domain driven (usage of business entities for encapsulating data and business logic)
- Database independent
- Scalable: no "long" connections towards the database
- One unit-of-work per use case (this one will synchronise with the database when committed)
- Multiple nested unit-of-work's (these will synchronise with their parent unit-of-work when committed)
- Unit-of-work should only synchronise the modifications to the data to the database at the end of the use case
- No persistence logic in the business entities
- UI-Binding logic should be as independent as possible of the business logic

>> Support for the following databases:
- SQLServer (use database ./Horses.mdf or generate one with script Horses_SQLServer.sql)
- Oracle (generate the database with script Horses_Oracle.sql)
- PostgresQL (generate the database with script Horses_PostgresQL.sql)

>> Used components:
- NHibernate (www.hibernate.org) as the ORM
- CSLA (http://www.lhotka.net/Area.aspx?id=4) for binding between the business entities and the UI and for facilitating the validation process

>> Two solutions exist, each having its own architecture:
1) Sol_HorsesWinForm: a two-tier WinForms architecture
    HorsesConfigurator:        The GUI application (WinForms)
    HorsesBusiness:            The business layer, containing the business entities and the service layer
    Framework:                The pieces that are reusable over different business domains
    Csla.NH:                Binding between the GUI and the business entities (stripped version of the Csla framework)
    NHibernate:                ORM
   
2) Sol_HorsesWebService: a three-tier WinForms - WebService architecture
    HorsesWebServiceClient:    The GUI application that consumes the web service (WinForms)
    HorsesWebService:        The web service
    HorsesBusiness:            The business layer, containing the business entities and the service layer
    Framework:                The pieces that are reusable over different business domains
    Csla.NH:                Binding between the GUI and the business entities (stripped version of the Csla framework)
    NHibernate:                ORM


DavidDilworth replied on Tuesday, October 09, 2007

Ok, I see where you're going.

I guess the fundamental decision you've made is that NHibernate is your "main" framework, with CSLA providing some support functions.  For example, you're using the dirty checking from NHibernate, not the CSLA version.

Our starting point was the other way round.  We wanted the "main" framework to be CSLA, with NHibernate simply providing database-neutral ORM persistence.  So we use the CSLA dirty checking, not the NHibernate version.

Both approaches (CSLA/NHibernate or NHibernate/CSLA) require some compromises to be made and some plumbing to be put in place to make certain things work.  But both are equally valid depending on your starting point.

In my opinion, using either framework individually, or both of them combined together, is far better than nothing at all - that's for sure!

skaue replied on Tuesday, October 09, 2007

Sounds to me configuring NHibernate to not lazyload is easier than to rewrite CSLA to CSLA Lite. Unless I've completely missed something (and I bet I am).

DavidDilworth replied on Wednesday, October 10, 2007

I would suggest that using CSLA with the DataPortal promotes eager loading, not lazy loading.

The aim is to fill the entire object graph for the required BO while it is on the Application Server (i.e. DataPortal Server) and there is a connection to the DB.  Once that is completed, the BO is serialized and "portaled" back to the client.

Once the BO is back on the client, and a piece of lazy-loaded data is accessed, then another trip across the DataPortal is required to fetch that data.

This means each BO should be designed more specifically to target the Use Case it is trying to solve.  And different variations of the same type of BO, may then have different object graphs inside them.

Some BO object graphs may be "big", some may be "small" - it will depend on the Use Case.

Copyright (c) Marimer LLC