Root/Child Object Design

Root/Child Object Design

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


BMT posted on Wednesday, March 21, 2007

I'm working on my first project using CSLA and have a general design question--I think my problem may just be a question of semantics.  Although parts of my question seem to be addressed in many posts, I couldn't find a definitive answer (if there is such a thing in the development world).

The basic question is this:  Is it okay for a root object (Business Base) to contain a "root" collection (Business List Base) of editable child objects (Business Base)?  i.e. Just because an object is below another object in the hierarchical structure doesn't necessarily make it a CSLA child object.

Here's a more specific example:

My top-level root object is an Account.  Below that are several collections of objects (such as Invoices, Orders, Licenses, Users, Notes, Equipment, etc.).  From a UI perspective, nearly all of these sub-objects will be edited by means of a detail form (i.e. not directly in a grid).  Should the overall design, then, be (1) Account is a root object, (2) the Invoices collection is a root object--even though it is contained within the Account object, and (3) individual Invoices are are a child of the collection?

I realize that this means that each each of the "root" collections in the top-level root would be responsible for retrieving/updating/deleting its own data (which is probably better anyway so that the data is loaded only when requested by the user).

Thanks in advance for any comments...

Bayu replied on Thursday, March 22, 2007

Hey!

I have scenario's implemented as you describe all the time. So let's see if I can be of help to you. ;-)

The 'recipe' I use is the following:
- I would have an Account BO that is an editable root object, so can be displayed, edited and saved independently of its children

- for each of the grids that will display 'child collection' I create a readonly list with readonly objects
    - e.g. InvoiceList and InvoiceInfo
    - these readonly lists are top-level objects, they accept an AccountID parameter in their factory method (andalso in their criteria and dp_fetch)
    - for convenience I of course put accessor methods on Accout (GetInvoices() {return InvoiceList.GetInvoiceList(Me.ID) } you get the idea)

- when users double-click in my grids, they get a details form where they can edit the properties of a single invoice, for this purpose I create a new editable root object, e.g. Invoice
    - this invoice can be saved independently of the parent Account

There is one issue that could arise here:
- you add/edit invoices and the changes are not reflected in your grid bound to InvoiceList/InvoiceInfo
- easily resolved though by reinvoking Account.GetInvoices and rebinding the grid as soon as a 'InvoiceAdded event' is raised by the InvoiceDetailsForm


What is the difference with your approach?
- well, I htink you tried to solve multiple use cases with 1 top-level BO
- what I basically did here is break this down into as many top-level BOs as you have use cases

Note:
- if you don't want Invoice objects to be saved to the DB independent of the Account (usually because you have validation rules for Invoice that also involve the Account or vice versa), then that is possible too:
- make Invoice an EditableChild and have a AccountInvoices EditableChildList on your Account
- in your UI you could maintain the EXACT same forms and views, it's just some logic (mostly save button logic ;-) ) that moves:
    - your Account details view stays the same
    - your Invoices grid is bound to the editable child list AccountInvoices, but the grid is readonly and does not offer a means to edit the details
    - you open up invoice detail views upon double click as before
    - there is only 1 save button that will save the whole object graph that start from Account

- there is 1 difference: your Invoices grid will stay in sync automatically, so you wouldn't have to refresh it using a new call to Account.GetInvoices

Mostly I prefer the solution with readonly lists since in my scenario's users hardly ever add/edit data, so it would be a waste to load fully editable objects all the time. It's a trade-off, because when they do I have grids that need to refresh. But this only involves readonly lists and objects, so they are reasonably cheap to refresh.


It has turned in a somewhat lengthy response. I hope it's all clear and of any help! ;-)

Kind regards,
Bayu

BMT replied on Thursday, March 22, 2007

Thanks for your response. 

I would still like to know if, according to standard CSLA design patterns and its intended use, it's acceptable to have a CSLA root object contain another CSLA root object (a collection in this case).  The users of this app will be editing/updating on a somewhat regularly.  Previously, I thought seriously about implementing a design similar to what you described, but (if it works to have a root be a "child" of a root and is acceptable) I would much rather have a single editable Invoice object.

So the question remains, is there a problem with a CSLA root object containing other CSLA root objects?  i.e. Account.Invoices (where Invoices is an editable collection and is NOT marked as a child).

ajj3085 replied on Thursday, March 22, 2007

The only problem I can think of is if you allow this, its possible for a client to call Save on the collection class.  Since Save returns a new object with the updated collection, the object which also contains the collection would no longer be looking at the correct collection... it would be seeing the collection before save was called.  That could lead to some bad things and hard to find bugs.

Usually what is done is that you provide a navigation method to get the other root object.  This is a method just placed on the object for conveince's sake.

So your Account class would have an instance method called GetInvoices, which returns the Invoices root collection.  However, even that may be an odd case; typically you'd probably have the navigation method on an AccountInfo class, which would be your readonly object.

BMT replied on Monday, March 26, 2007

After thinking about this a little more, I've decided to go back to my original design (and your suggested implementation).  I'll have a read-only collection of read-only "XYZinfo" objects.  Each of these objects will have a "GetXYZ" instance method to get the root XYZ object.  I guess I'm just not used to creating multiple objects that contain the same data.

Copyright (c) Marimer LLC