Where Should the Metadata Go?
At first I put config info into the appropriate cfcs. So database info for the User object (e.g. tablename) was in UserDAO, general info for the user object required even before a User existed (such as the property name list) was stored in the UserService, and info relating to the User object and its properties was in the User business object. Same for pages, articles, categories, products, etc. This worked, but there were just too many files to edit so it started to become a pain (a) to be sure exactly which object contained a given piece of metadata and (b) opening and editing all of the necessary files.
Then for the next project I tried putting all of the metadata in my LightWire config file so I could set all of the properties for an application in one file. It only took one e-commerce project to realize that was a non-starter! While I create applications very quickly, they are *not* limited to trivial apps and by the time I had all the config info (even using a programmatic config file) for over 30 business objects it was pretty clear to me that sticking all the config data into a single file wasn't optimal (my scroll wheel is still suffering from the abuse I gave it on that project!).
Now I've decided to try the (fairly popular) idea of a separate metadata object for each object. So, in addition to UserObject, UserService and UserDAO, I'm adding a UserMetadata which is injected into the user object, service and DAO allowing all of them to access metadata that it available in a single file. I'm not convinced about this design decision. I considered just putting the metadata into the business object, but sometimes I need to know something about a user object before I've actually loaded the object so I don't want to be forced to create a User just to know what kinds of properties it can have. I also want to have all my config info for an object in one place (which would include database stuff like the table name) and I'm not comfortable putting database specific information into the business object. There *is* a story to spreading the config info between the service, DAO and business objects, but as I mentioned above, in my experience that just became a pain over time so while it might be the "purest" choice it didn't really work for me.
For now I'm going to go ahead with this approach, but I'm going to use delegates to hide the design decision so if you want the title of a property you can call User.propertyTitle('FirstName') rather than explicitly calling User.get('UserMetadata').getTitle('FirstName'). In this way, if I need/want to back out of the decision it won't take that much refactoring. Of course, with LightWire I also easily have the ability to put all the config data into a config file if I want to down the line using constructor or setter injection into the metadata objects.
I'm also going to treat the metadata objects as if they are "final" (constant) by making all instance variables private and only providing getter access (not setter mutators). If I ever need to be able to script the creation of the metadata (say pulling it from a database at run time instead of generating the metadata files at generation time) I'll be able to do that in the LightWire config, so I see no reason to make the metadata appear to be run time configurable.
Anyone else have any thoughts around any of this?





Last night, I had dreams about this and the other post on which I'm about to comment. Strange, huh? It must have been the Nyquil. =)
In any case, as I know you like feedback (as do I - it's the reason I blog and comment), I thought I'd offer some.
1) You mentioned that "it started to become a pain (a) to be sure exactly which object contained a given piece of metadata"
Of course, I haven't seen the code so I can't say for sure, but I'd ask "why is it a pain?" Is it only a pain from a coding standpoint (alluded to in your (b) pain point), as in "I don't know which file I need to change," or is it a pain because you don't know where to access that data from (as in "I'm programming in module A and I need metadata, but I don't know if it's in object B, C, or D"?
I hope my question makes sense. If it is the latter, you might look at your coupling - should A even need data from B, C, or D? I'm fairly sure you've already done that, though, I post only as a reminder in case you forgot =).
The metadata object sounds like it has been helpful. It may very well be a good idea. But, it seems strange to have data about an object's apart from it - I mean, it _is_ an object, and part of an object's purpose is to contain data and the methods that operate on it.
Of course, it's hard to tell where the metadata blends into the data. My views and models share a lot of metadata - as in, the model knows "this is a date column, so it needs to validate as a date, except if it is empty and I am supposed to allow nulls." The view also needs to know "this is a date field, so I should display a calendar or a select field for month, year, and day."
Is that metadata? I'd say so. But, does it belong in another object? I'm not sure. In this simple case, I'm opting not to (so far). But, your case seems much more complex, and may require it.
On the other hand, I want the programmer to be able to say "this is a phone number field" rather than "this is a nvarchar(15) field." The model should validate that it meets some format of phone numbers, but the view shouldn't need to change (unless I wanted to separate area code, prefix, and such, which I don't currently). So, they should make that change in the model. But, there are cases where a programmer wants to make a field type "hidden" or "null", where the field should either be hidden in the form or not exist in the form at all. But clearly, the model still wants those fields. In this case, only the view should be edited. But, should I require the programmer to remember which fields are view-only and which are model-only, or should I allow it anywhere?
I haven't come to a conclusion on that, which is why this post interested me so much. I hope it made an ounce of sense, at least.
Thanks for the feedback - that Nyquill can be powerful stuff. Still remember the night when I had a lousy cold and learnt that there *is* a difference between Nyquill and Dayquill (hey, I'm from the UK - I didn't know!) and that the latter should not be taken just before going to bed :-<
Re: the metadata object, it is actually working out really nicely for me. While I agree that an object should indeed contain its data and validations, it should not necessarily contain its own metadata and I'm finding the code if flowing nicely by having a single config object for each business object with an init that currently looks something like:
// Basics
set("ObjectName","Page");
set("ObjectTitle","Page");
set("PropertyNameList","PageID,Title,Name,FilePath,Feature,HTML,DefaultAction");
// Database
set("ViewTable","pfn_Page");
set("ManageTable","pfn_Page");
// Admin
set("AdminSingleActionList","Add");
set("AdminObjectActionList","Edit,Delete");
set("AdminListPropertyNameList","Title,Name,FilePath");
// Properties: Title, data type, optional name, optional short description
addProperty("Page ID","ID");
addProperty("Title","Title");
addProperty("Name","Name");
addProperty("File Path","String");
Obviously this is a very partial list as I'm rebuilding this from scratch, but it is working out very nicely with the metaconfig object handling all the metadata and being injected into the business object, the service and the DAO.
Will keep you posted how this goes :->
I'm glad it seems to be working for you, and I'd love to hear more as you learn it. My metadata won't be hard to extract if that turns out to be the way to go - I've just kept it in an in-memory query for easy access. Of course, that could change if I experience significant performance problems, but for now it has been doing just fine. I have to admit though, thinking about it, I might make life easier on myself by abstracting that stuff into another class. I guess we'll see.
Will keep playing and let you know how it goes!