
Don't Skimp on Your Metadata!
I'm sick of shoddy metadata. All of the popular web frameworks insist on repurposing existing artifacts as metadata. With Rails, the database is the metadata. Grails? Domain classes. Don't get me wrong: both solutions are vastly superior to the old EJB-style "change 7 files to reflect a new attribute name" paradigm. But metadata is the core of scaffolding, and generation of that scaffolding is critical in staying DRY. As far as the framework is concerned, your metadata is the authoritative representation of your application's design, so using an artifact that was never intended to do so is playing with fire. The problem? There are concepts that should be modeled in metadata that can not be represented by a database table. Example: many-to-many relationships.
The student-teacher relationship is a familiar many-to-many relationship. Teachers teach many students, and students are taught by many teachers. Rails provides two options for the teaches association: has_and_belongs_to_many and has_many :through. HABTM is simpler, though less rich - HMT allows for additional attributes on the relationship table. Setup looks like this:
- Create a new table in the db (the convention is ‘students_ teachers') with teacher_id and student_id columns
- Add
has_and_belongs_to_many :teachers, :class_name=>"Teacher"to student.rb - Add
has_and_belongs_to_many :students, :class_name=>"Student"to teacher.rb
The end result? Your metadata is now scattered across three artifacts! There's simply no way to model many-to-many in a database table, so you have to mark up your domain classes with the necessary extra information. Grails is also a three step process - just substitute ‘write a new domain class' for step 1.
We didn't want TerraFrame MOJO™ to burden our domain classes with metadata leftovers, so we created an xml schema to represent all metadata as a single artifact. Here's the code for the same relationship in MOJO:
<mdRelationship name="demo.school.Teaches"
label="Teaches"
description="Teachers teach Students"
composition="false">
<parent name="demo.school.Teacher"
cardinality="*"
label="Teaches"
method="teaches" />
<child name="demo.school.Student"
cardinality="*"
label="Is Taught By"
method="IsTaughtBy" />
</mdRelationship>
Since we created it for this specific purpose, our relationship metadata is complete - no has_many or belongs_to required! Further, you use the exact same tag to create any kind of relationship you want: one-to-one, one-to-many, many-to-many. Just change the cardinality! In fact, the composition="false" attribute is completely unnecessary here, as false is the default value. I just threw it in to show how trivial it is to add the cascading delete functionality that composition implies.
To be fair, the Rails/Grails approach has its advantages, chief among which is the avoidance of learning a new schema. You already know how to create database tables. You don't know MOJO's metadata XML (though we've tried very hard to make it readable). Further, the majority of modeling concerns can be represented in a domain class or metadata table, and for quick and dirty apps it's acceptable to sacrifice some cohesion for rapidity of implementation. Nonetheless, you have to write your domain model somewhere. With Rails, you write your database code and it generates ruby classes for you. With Grails you write your domain classes, and it generates the database for you. With MOJO you write your metadata XML, and it generates the database and your domain classes for you. And let's be honest: you had to look up has_and_belongs_to_many the first time you used it, so once you cross the line into domain class markup, the familiarity advantage disappears. In fact, since MOJO uses XML, you get content assistance to help you figure things out.
At the end of the day, any real-world project will contain some corner case that can't be satisfied by a repurposed metadata model, and changing 3 files to represent one relationship doesn't feel very DRY to me.