Tuesday, October 18, 2011

Evolution of a data model

So I've established in previous posts how sometimes it's prudent for me to use collections of objects in our application. In my attempt to figure out the right relationship between service/data/value objects, I have went through far too many way too embarrassing designs to belabor; the fortunate thing being that none of them made it to production.

In my 1st approach, I wasn't really thinking about having a service layer. My desire was that app should just be able to ask for a property, and get a guaranteed response--it should never have to know what method to call in order to populate that object ahead of time. This was the frame of mind that I was trying to codify in my "Iterating Business Object" post. This meant that the value object itself knew how to get its own value if one hadn't already been defined.

But I have abandoned that and decided to use the more conventional service model approach. The discussion about "don't come to me, I'll come to you," though only vaguely related kind of convinced me that this approach was an acceptable one. Admittedly, some small part of my mind still thinks the former approach would be really cool, but being a noob, I defer to the currently accepted best practice here.

So I mentioned how we have 100 or so products, and about 3 major subjects, and that when you provide a subject to a product, you end up with a report. The thing about reports is that they also heavily cross-link to other reports (a la, "want to more details? try this report..."). And, of course, part of the search interface involves the user choosing a report for the subject. So in a single request cycle, we are dealing with details about more than one product (but only ever one report--we need to build links to the other reports, but don't need the actual handler, data, and view(s) associated with it).

So this has become my congealed approach to handling value objects, services, and collections. I have a service object, which is a singleton, and has a bunch of methods that talk to the data layer (singleton) to get product details, populate those into product objects (request scope, service uses the "provider" concept to get these objects), and add each product object to a collection (another provided object in the request scope). The service always checks whether the product is in the collection first before adding it. The implementation doesn't ever talk to the collection (shouldn't anyway), it talks to the service which returns either a single object or an array of objects.

I believe this is fairly standard, and it is working really well. We have about 6 different ways to get products (by id, by the report URL, but subject types...). To facilitate this in the collection, whenever I add a product, I index it against the necessary keys for getting it later. This way, I don't have to loop through the array holding all the products each time. The index is just each value in the key and the corresponding product element index in the collection array.

The problem I ran up against today was the need to iterate over the collection in groups. Obviously, this is really easy with queries. Additionally, if it ever becomes necessary to retrieve products by a different index, then I have to create a new access method, as well as a method that will create/populate the index when its added. This seems a bit too procedural. What'd be really cool is a generic getProductBy(property, value) method, but then I need to have an index on *every* property! And what happens when I need to get products by more than 1 criteria, which the way our business works is bound to happen sooner or later?

So super-awesome thing: query column values can be set to objects =0 So I haven't actually built it out yet, but maybe the internal storage mechanism for the collection could actually be a query, instead of an array--after all, queries are generally super-efficient in Coldfusion. Each property on the object would be a column in the query, with an additional column holding the object itself. This makes it so simple to get only the products needed from the collection, even if there's more than one criteria. It also makes it easy to order the collection, which can additionally facilitate grouping. The grouping itself would still need to be done in the implementation (e.g. by comparing the current object to the previous one), and the all the properties about the value object would be stored in 2 places (in the object itself as accessors, and in the collection as a query). Once the products where selected, the column holding the object could be converted to an array via either listToArray(valueList()) or a simple loop...

I'm stoked. Hope this works out well. The only thing left then is some sort of consistent pattern for providing micro-data sets (which usually end up being children of larger value objects) without needing to build out the entire service/collection/value-object model. And related to that, I continue to run into instances where these micro data-sets have the same properties, but are accessed differently/separately in the application. Maybe they do just need to be built into collections of objects... ugh... that seems like overkill.

No comments: