A common object, architecture ramblings required!

A common object, architecture ramblings required!

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


pfeds posted on Tuesday, August 26, 2008

I've created a library for an online store and I'm having a bit of a design issue with a couple of objects that are widely used within the library, namely Product, but also others such as Customer.

For Product I have "Product", "ProductList", and "ProductInfo".  This works fine.

A number of other objects also require product information. For example BasketLine and OrderLine need to know the product name, image, whether it's in stock, etc.  Other objects for product reviews, product bundles, etc also need to load product information. 

Until now I have always used joins in the sql statements to link to product information in read only lists.  For editable objects I have added a non-serialized property to load the Project object and access the properties, such as:

[NonSerialized]

Product product;

public ProductName

{

   get

   {

      if (this.product == null)

      {

         this.product = Product.GetProduct(this.productId);

      }

      return this.product.Name;

}

To me this seems really inefficient as loading the product object additionally loads a number of child collections.

What I think I want to do is create a parent readonlybase ProductSummary class that reads in the property information as well as loading a child read-only collection of product variants (such as different sizes/colours etc).

Then, with the other objects I can create a similar nonserialized member and lazy load it with ProductSummary.GetProductSummary(productId);

I'm not completely confident that this is the best way to go about it, so I'm wondering if anyone else has come across this and has any good ideas.  It's vital that the library is efficient and doesn't carry out unnecessary processing.

Thanks.

rsbaker0 replied on Tuesday, August 26, 2008

You can still use your joins -- this might be a good idea if you will *always* need the ProductName. I have similar situations in an implementation I am doing.  When the main object is fetched, the linked properties are fetched via a left join (inner would be fine also), but these are of course read-only and cannot be updated.

We also support lazy-loading of both objects and individual properties if the property is frequently used. In your post above, for example, why fetch the entire product when you just need the name. (e.g. SELECT PRODUCTNAME FROM PRODUCTS WHERE PRODUCTID=...)

pfeds replied on Tuesday, August 26, 2008

The problem I encountered that caused me to think about this was to do with stock control.  This can be configured in a variety of ways - on/off, in/out of stock flag, product or product variant level, or driven by a live stock count.  I want to display in stock/out of stock information intelligently, such as in the basket view and order view. I also would like to use it to show/hide special offer bundles depending on an item being in stock. I don't want to copy this code, although I could put it on Product as a static method such as public static StockStatus GetStockStatus(IProduct product);

As well as this comes the issue of a product possibly having a number of variants.  If this wasn't the case then a simple left join would do the trick, but now I would like to display a list of variants in certain places such as the basket (to make it easier for the user to change) and when selecting product variants within an offer bundle etc.  What I don't want to do is expand all these objects (basket, bundles, etc) to read in child collections of product variants and such.

With the current objects I have this would mean loading each Product (say on the basket view) which in turn would load a fair wack of unnecessary information and slow the site down, especially if there are a lot of items in the basket.  Additionally, when proceeding to submit the order I map the basket lines to order lines which will load all the products and copy information (in case of price changes etc) - this seems to be a fairly slow process (well, slow in my opinion!).

This is what led me to the idea of a read-only "summary" object that would only read in the basic product info and variants.  Then I can expose this as a lazy property in all the required classes so that they can retrieve the product information if desired.  I think it would probably work well as (a) it will reduce the need for sql joins, (b) reduce the amount of properties in each object, and (c) make maintenance a lot easier, and so on.

I think it's the right way, but I'm just wary of any hidden nasties that may crop up with this idea!!

richardb replied on Tuesday, August 26, 2008

Designed a simliar system too.  While I had a full blown Product object with some child objects, I also created some Read-Only lightweight objects that would give ProductID, name, description and stockqty available, the latter being calculated in the SQL stored procedure.  This helped performance when adding items to an order.

Mind you, when reading back the order items (the order item record would just have the ProductID and Price) the SQL would do a JOIN to get the product description from the products table - getting the sundry information like this would be quicker than having each order item business object loading up it's own product object.

But we also extended this to include a lazy loaded lightweight product property so that if the user did change the productID, we would load the new product and set the order item product code, description, price, etc.

The Stock Qty Available was only used as a snapshot and not relied upon for allocating stock to an order.  I did most of the stock allocation work in a Stored Procedure as that seemed to be the best place to do that for my solution.

Also had a RelatedProducts collection so I could automatically add these items to an order as the "main" item was chosen.

Sounds like you are on the right track to me.

 

Copyright (c) Marimer LLC