Object Model Design Question

Object Model Design Question

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


bnair posted on Thursday, June 15, 2006

Hi All,

I am working on CSLA 2.0 and I am running into object model design issues.

I am building an address book module..

I have the following classes

AddressBookContact - Editable Root
AddressBookContactList - Editable Root Collection

AddressBookGroup - Editable Root
AddressBookGroupList - Editable Root Collection

When I fetch the AddressBookContact object I need all the groups that the contact belongs.Do I need to make AddressBookGroup and AddressBookGroupList as switchable objects

ALso ,when I fetch the AddressBookGroup object I need all the contacts that are in the group.In that case I need the ContactList collection..

How would I model this efficiently without ending up creating a lot of redundant root and child classes?

--bnair

david.wendelken replied on Thursday, June 15, 2006

I'm not pretending to be an object modeling guru, I'm still learning (and my professional experience has been in the dreaded data-centric world).

An AddressBook is composed of AddressBookContactList and AddressBookGroupList.

An AddressBookGroup is composed of AddressBookContactList and AddressBookGroupList.

Not sure whether it would be a good idea to create an AddressBookMember and sub-class it to AddressBookContact and AddressBookGroup.   Just don't have the experience in building object-based systems to know yet...

Hope that gives you some ideas to chew over! 

Ronnie replied on Friday, June 16, 2006

bnair, this is the problem I bump into a lot when trying to align model with implementation. It is often said that use of switchable is a sign of bad design, and the most common suggestion is to fix it with a readonly collection with simple readonly childs, and when you need to edit you instantiate a new editable version of the "same" domain object, eg:

AddressBookContactList<ReadOnlyListBase>
-- contains AddressBookContactInfo<ReadOnlyBase> objects

AddressBookContact<BusinessBase>(root)

However, as you mention there is often a lot of duplicated effort in this, mostly fields, property-getters and get/fetch-methods. I too am wondering and looking for a better solution, as it just feels wrong having to create different objects for readonly/editable/child/root scenarios. It seems to me there is a lot of cases where you need to have a root object, but still need to have the possibility of having a collection of that type.

 

SonOfPirate replied on Friday, June 16, 2006

Not sure if this truly follows the concept of behavior-oriented design, but the approach that we've taken to this VERY common scenario is consistent with what is shown in the sample app in the book.  The explanation there is to design a class that represents the relationship between objects as the relationship IS a behavior separate from the objects and, in many cases, is represented with a many-to-many table in a database.  Because adding/modifying/removing a record in this N-to-N table is adding/modifying/removing a relationship, this object is able to handle that function.

In your case, what we would do is have the following classes:

AddressBookContactList
AddressBookContact
AddressBookGroupList
AddressBookGroup

AddressBookContactGroupList
AddressBookContactGroup
AddressBookGroupContactList
AddressBookGroupContact

Let's look at one direction through the OM:

AddressBookContact contains a Groups property of the AddressBookContactGroups type.

AddressBookContactGroups is a read/write collection of AddressBookContactGroup objects.  It accepts a reference to the parent AddressBookContact object in its constructor and uses that object's ID value as a parameter when selecting the child records to populate the collection.  The objects that are actually instantiated are of the AddressBookContactGroup type which actually represent the relationship and NOT the group itself.  So, in essance, you have a read-only list of groups (if you choose) but maintain a read/write list of relationships.

This is no different than what Rocky has done with the ProjectTracker application.  See the Project->ProjectResources->ProjectResource classes for examples.

The question that has perplexed us; however, is how to simplify this model for the developer so that the complexity is abstracted?  For instance, to access the properties of a particular group that a contact belongs to, the following would be the most intuitive for a developer:

myAddressBookContact.Groups[0]

However, this returns the relationship object and NOT the actual group object.  So, the following code would actually fail:

= myAddressBookContact.Group[0].Name;

Instead, the developer would have to use the following statement:

= myAddressBookContact.Group[0].GetGroup().Name;

Not very intuitive is it? And not very consistent with how the same operation is performed in other areas.

So, what we've done to accomodate the needs of our developers, is to incorporate an interface (IAddressBookGroup) as the return type for the collection's indexer, implement it on both group classes (AddressBookGroup and AddressBookContactGroup) so that the first statement will work.

Accomplishing this, however, does complicate things a bit in the AddressBookContactGroup class.  Instead of maintaining just the ID of the AddressBookGroup object, we actually maintain a reference to that object, instantiated within the data access code.  Then, all of the properties specified by the interface, such as Name as shown above, are implemented by simply delegating to the inner AddressBookGroup object.

The end result has several benefits that thus far, for us at least, have outweighed the extra complexity with these classes.  First, you accurately encapsulate the behavior of the relationship between the objects, but you also gain the ability to manage that relationship through the AddressBookContactGroup object.  For instance, when changes are applied to this object, it is the relationship that gets updated - so when a new group is added to the collection, a new record is added to the appropriate N-to-N table in the database.  In addition, because we delegate to an instance of the AddressBookGroup class within our AddressBookContactGroup class, any changes made to the group's properties are managed by the AddressBookGroup class - providing consistent encapsulation within the model.

I think the only real difference between our approach and Rocky's is referencing the object rather than exposing a GetResource() method as is done in the ProjectResource class as well as using interfaces to define our return types.  It's up to you which approach makes more sense to you.

Hope this helps.

 

kbcb replied on Saturday, February 17, 2007

I just recently (today) posted a thread on the forum relating to a very similar issue. For the most part, I came up with the same solution... However, I am wondering, how do you databind these RO object properties to a form. How do you databind a list of groups to a combobox, where you want to also databind the combobox's selection to a property that is a RO replica of the group object (which was created to represent the relationship of the group object)?

 

Thanks!

Copyright (c) Marimer LLC