NOTE: work in progress! this is not a thing yet!
This is an attempt to define a somewhat standard interface to create the model
part of a system. In a sense it is kind of an abstract of the
active record married
Promise and decoupled from the actual persistence layer.
The idea here is to define an standard, which then can be reimplemented with different persistence layers underneath it. The point of all this exercise is to decouple the application level code from the persistence layer; and bring balance to the galaxy.
Why Active Record
Active record, as any other, has its drawbacks. But, regardless of those it is almost unbeatably good at one thing: representing tabular data.
In the end you don't have to build your app logic on top of active record. In many cases it is simple enough to do so, in some it isn't. But, regardless to the case, it is much easier to talk to an active record than to a database directly.
Don't fight it. You can't win this one....
modelier there is an idea of I call
schema. This thing is basically your
mapping to the persistence layer. It is not supposed to have any actual business
logic of your application. A schema simply outlines units persisted properties
and relationships between each other.
;const Model = url: "mongodb://localhost..." ;const User = "User"email: Stringusername: Stringpassword: StringcreatedAt: DateupdatedAt: Date;ModelindexUser "username";const Post = "Post"urlSlug: Stringtitle: Stringbody: Stringauthor: User // <- direct associationcreatedAt: DateupdatedAt: Date;ModelindexPost "urlSlug";ModelindexPost "createdAt";const Comment = "Comment"post: Postauthor: Usertext: StringcreatedAt: Date;ModelindexComment "post";ModelindexComment "createdAt";
There are a few important moments to consider. Firstly, primary ids are implied.
Same for relationship references, they should be done on a model to model basis
and actual references should be consistently auto-generated into
such by the engine.
The generic indexes interface should look somewhat like this:
const Provider = ...;ProviderindexModel "field";ProviderindexModel "field1" "field2";
Some specific providers might add some extra options that are related to the databases they manage.
If a model has a
updatedAt properties defined in a schema,
those properties will be automatically populated.
All models will have a standard CRUD operations interface regardless of the actual
persistence layer provider. All those methods will return an instance of
and are supposed to be used with the ES7 async/await functions.
var user = await username: "nikolay";await user;await user;
There are also a set of similar operations that can be performed on a whole table
await User; // make _everyone_ an adminawait User; // delete everything
NOTE: the table level operations are combinable with the query filters, see below.
modelier consists of basically filters and resolvers
// to find a record by an IDconst user = await User; // NOTE: rejects into NotFoundconst admins = await User; // or #.all();const admin = await User; // also #last();
#filter() method returns an extended
Promise which has a bunch of
chained methods to aggregate data:
const admins = await User;const names = await User;
NOTE the promise that was returned only trigger the actual query once the
then method is called. Before that happens you can chain it as much as you like.
#filter() method can take the following parameters:
You also can use the implicit schema references between the models with the
const user = await User;const posts = await Post;
This will automatically resolve the external key references and build a correct query to the database.
Ordering, Grouping, Aggregation
The query language also has several methods to describe various ordering and aggregation queries:
const latest = await Post;const counts = await Post; // #avg("rating")...
Most of the method names are derived from the
actually are lazy methods for the querying language.
Copyright & License
All code in this repository is released under the terms of the MIT license
Copyright (C) 2016 Nikolay Nemshilov