The Persistence Layer (PL) is that “thingie” in your app that communicates with another API to store the data we want.
In our case, we communicate with MongoDB for that. Therefore, an instance of
Mongo.Collection is our API to communicate with the Persistence Layer, therefore in our app we can regard collections as the PL.
What if at some point in the future, you decide that a certain collection should not be kept in MongoDB but rather used via an external HTTP API?
Your code architecture should try to be as independent as possible from the PL, because this opens the path for scaling and decoupling later on.
Just try and think this way, and whenever you want to start a new project: disregard the way you are going to store the data, focus more on the app/business logic layer.
Then build your database on top of it.
Since we want to explicitly separate it, I propose that instead of storing collections inside
/imports/api, we give them their own folder
Now let’s assume you have a new collection for comments on posts, and it’s used only for posts. What do we do ?
Should we store it in
/imports/db/posts/comments/collection.js or in
It’s a matter of preference here. I prefer the first approach, but the collection’s name I use is
we know it’s context. But this only applies if my collection is coupled in a way to posts, if not, then go with second approach.
We need to be able to validate easily the data we save in a collection to prevent bad things from happening.
Read more about it here: https://github.com/aldeed/meteor-collection2-core then come back!
Now let’s create a schema for our
Now let’s attach it:
Nice! This structure works fine. This will never allow you to insert or update a document without validating it properly.
Beautiful, but now we see those “tags” in there, and we know from our previous chapter, that we need a way to re-use them properly, this is why we introduce here to concept of Enums.
An Enum is a set of constants, and they should be in their own module. Let’s try an example
The value of the enum, represents how you internally want to store it. Does not represent how you want to display it. Keep that in mind.
And if we go back to our schema it can look something like:
Another quick example to ilustrate their purpose, imagine a status of a Task:
The rule using enums is that when you are dealing with them, you must never use the string/value itself.
In your code, client or server, when you have to see if a status is active:
Keep in mind that you can use Enums anywhere, not only for storing stuff in your db. Whenever you find there’s a list of constants needed, you are looking at an enum. And ofcourse you can use them client-side or server-side.
This collection feels like it is from another world, it’s best that we standardize it as well, and also give it a schema:
A sample schema that works with Accounts configuration:
And don’t forget about this nice package: https://github.com/alanning/meteor-roles. Allows you to nicely manage roles in the system.
You may want to do something after an element is removed or updated or inserted from a certain collection.
The almost official way to do this is by using this awesome package: https://atmospherejs.com/matb33/collection-hooks
Give it a read, you’ll love it.
My recommendation here is to dispatch an event on a given hook, and store the hooks inside
and the logic for handling the hooks should be inside
Most likely, your collections will store the user who created something, or store things like when it was created, or when it was updated.
So instead of creating hooks for every collection, it’s easier to define a behaviour and re-use it.
Please read about it on these links: https://github.com/zimme/meteor-collection-behaviours/
For storage, I think it’s fine to just attachBehaviours where you store the collection:
Don’t be afraid to extend your collection, especially when you need something that is specific to the collection. For example, you have the
_id of an user,
and you want to quickly get the email:
With the use of this package: https://github.com/dburles/meteor-collection-helpers you can make your documents smarter if they are fetched through your collections.
This will allow you to easily do:
Create an index for your collections:
This will allow you to use it like this:
You can go ahead and even put enums inside index.js, but be very careful with their naming,
as they need to be specific:
How do we work with relations in a non-relational database? We use Grapher, ofcourse. And even if we don’t have relations, we still should use Grapher because of the way it works, as you will see below:
Here’s how simple it is:
We need those links loaded, so aggregate all links importing into a single file. Import this file in the client-side and server-side init modules.
And when you want to get the post alongside with the user’s profile for example:
Grapher is a very complex tool and it can work with queries client-side as well. You can have reactive queries, there’s simply a lot related to it, but it’s properly documented here: http://grapher.cultofcoders.com.
Begin with the Guide, and patiently read through it. Once you read the guide you can understand the following patterns:
The recommended way of working with Grapher is by working exclusively with NamedQueries. If you would like to store and expose NamedQueries:
Don’t be afraid to create shortcuts for all your queries in one place:
Now that we created the query, we need to expose and secure it:
Aggregate it in one file and import this file on server-side only.
Now from the client you can easily do something like:
By abstracting the data retrieval layer to Grapher and allowing it to do the linking of collections for you, you will find yourself with so much less headache. Because Grapher forces you to specify only the fields you need, you will find your apps running fast and secure right from the start.
You may feel that having your query in
/imports/db and your exposure inside
/imports/api to be a little odd.
This is why you can put your query inside
/imports/api as well. But the reason for not putting exposure inside
because it contains app logic, and that layer it should be independent from it.