Please jump ahead if you are not interested in reading some philosophical BS I could not let go without saying
Ramblings
Well, an argument around ORM, abstractions, domain modelling and OO (needless to say in a fundamentally functional world of javascript on top of an essentially non-relational nosql world) is a rising discussion, even among the likes of Martin Fowler and Joel Spolsky.
So I’m not going to go there
But (there always is a but), coming from the java (ee) world with ages of experience in database and domain abstractions like ejb (yikes!), ibatis, ebean, hibernate, jpa and patterns around daos, interfaces, implementations and layers upon layers, I’ve come to take refuge in javascript’s simplicity which we once had with fortran, cobol, pascal etc.
Come to think of it, I find it much better, rather natural, to think of a business domain in terms of units of data, its properties and its mutators. I’ve also yet to see a truly database-agnostic crud-based business application that’s not built around either one of a service oriented or message driven architecture. I’ve also seen less than a handful applications (among hundreds) that actually changed database vendors.
Regarding data formats, neither of those packages that I’ve set out to use actually describe how data is persisted. They are merely javascript object literals that either validate (set), mutate, or get by the app container’s functions which are pure javascript. It so happens that mongodb api is fully javascript compliant.
In fact, the closest that gets to an actual OO abstraction in meteor/mongo world is https://github.com/jagi/meteor-astronomy which keeps everything defined within a single Astro.Class
and replaces all the packages I’ve referenced in my question. But hey, I’m not feeling comfortable enough with Astronomy to start off a project with it.
Jump here if you feel like reading, but skip ahead if you want the TL:DR
So yes, I believe that the properties defined in simple-schema and collection-helpers do belong in the same file, if not in the same object literal because the simple-schema describes what’s on the db and collection-helpers contain/describe:
- some calculated transformations
- some getters
- some custom type castings
- relations to other database objects
- some potentially denormalizable references
which in fact, in a traditional modern OO database abstraction/model layer, would have been kept within the same class only to be annotated (java) accordingly.
Jump here
I would have loved providing an actual example, but the app I’m working on is a very specific domain with names and properties that makes no sense to anyone who has not been given a proper introduction to, hence the trivial (and kind of silly) posts/comments example.
So, getting back to the original question, I’m looking for a way to do the following (or something similar)
**the posts “object”: **
// Stock meteor (client & server)
Posts = new Mongo.Collection('posts');
// Simple schema (client & server)
PostsSchema = new SimpleSchema({
title: {type: String},
authorUserId: {type: String}
});
// Collection2 (client & server)
Posts.attachSchema(PostsSchema);
// Collection helpers (client & server)
Posts.helpers({
// For example, this could very well be considered for denormalization some day
latestComments: function() {
return Comments.find({}, {sort: {commentedAt: -1}, limit: 3});
}
});
**the comments “object”: **
Comments = new Mongo.Collection('comments');
CommentsSchema = new SimpleSchema({
postId: {type: String},
commentedAt: {type: Date}
});
Comments.attachSchema(CommentsSchema);
Comments.helpers({
postAuthorFirstName: function() {
var userId = Posts.findOne(this.postId).authorUserId;
return Meteor.users.findOne(userId).profile.firstName;
}
});
So, when I decide to separate posts and comments into their own packages, posts requiring the comments export and vice versa become a circular dependency problem and I need an elegant way to solve that, while keeping the code readable in such a way that the properties of a post and the properties of a comment are self-evident.
PS: we are not discussing if making postAuthorFirstName
or latestComments
methods on their own would be better. For sake of argument, assume that they are inseparable properties. In fact, let’s assume that they are https://github.com/aldeed/meteor-collection2#autovalue autovalues that reside within the schema that need to be inserted within the same transaction, and not requiring a collection hook.
PPS: I’m not getting into publish composite, but what it does is (obviously on the server) publish the related cursors along with the requested primary cursor. In our case, it would run the same find queries from the helpers to fetch and publish a cursor containing the top 3 comments along with a post as well as the post and author profile of a comment.