grapher/docs/structure_and_patterns.md

138 lines
No EOL
4 KiB
Markdown

# Structure and Patterns
These are a set of recommended ways to handle things, they do not enforce
anything.
We suggest you read: http://www.meteor-tuts.com/chapters/3/persistence-layer.html first.
- Store queries inside `/imports/api` under their own module and proper path (eg: `/imports/api/users/queries/getAllUsers.js`)
- Store `links.js` files inside `/imports/db` close to their collections definitions. (eg: `/imports/db/users/links.js`)
- Create a an `/imports/db/index.js` that imports and exports all your collections
- In `/imports/db/links.js` which imports all links from collections (eg: `import ./posts/links.js`)
- In that `/imports/db/index.js` also `imports './links'` after you imported all collections.
- Make sure you import `/imports/db/index.js` in both client and server environments.
- For Named Queries, keep `query.js` and `query.expose.js` separated.
- Create an `/imports/api/exposures.js` that imports all `.expose.js` files, and import that server-side.
- When you import your queries suffix their with `Query`
- Always `.clone()` modular queries before you use them client and server-side
- Store reducers inside `links.js`, if the file becomes too large (> 100 lines), separate them.
- Store server-side reducers inside `/imports/api` - as they may contain business logic
If you respect the patterns above you will avoid having the most common pitfalls with Grapher:
**Reducer/Links not working?**
Make sure they are imported in the environment you use them client/server.
**My link is not saved in the database?**
Make sure you added it correctly to your SimpleSchema, if you have that.
## Fragments
You will find yourself requiring often same fields for users, such as `email`, `fullName`, and maybe `avatar`.
For that let's create some fragments:
```js
// file: /imports/db/fragments/UserPublic.js
export default {
fullName: 1,
avatar: {
path: 1,
},
email: 1,
}
// file: /imports/db/fragments/index.js
export {default as UserPublicFragment} from './UserPublicFields';
```
Now use it:
```js
import {UserPublicFragment} from '/imports/db/fragments';
Invoices.createQuery({
number: 1,
total: 1,
user: {
...UserPublicFragment,
billingInfo: {
// etc
}
}
})
```
You can also compose certain fragments:
```js
import {compose} from 'meteor/cultofcoders:grapher';
import {
UserPublicFragment,
UserBillingFragment,
} from '/imports/db/fragments';
Invoices.createQuery({
number: 1,
total: 1,
user: {
...compose(
UserPublicFragment,
UserBillingFragment
)
}
})
```
`compose()` uses deep extension, so it works how you expect it to work, especially if some fragments have shared bodies.
Do not use special properties inside fragments, such as `$filters`, `$options`, etc.
## Scaling Reactivity
If you want to have highly scalable reactive data graphs, think about moving from tailing MongoDB oplog to RedisOplog:
https://github.com/cult-of-coders/redis-oplog
Grapher is fully compatible with it. You can configure $options, inside the $filter() on to allow namespaced watchers.
Sample:
```js
export default Messages.createQuery('messagesForThread', {
$filter({filters, options, params}) {
filters.threadId = params.threadId;
options.namespace = `thread::${params.threadId}`;
},
text: 1,
})
```
Or, if you don't want to expose that, embody the `$filter()` server-side:
```js
// query.expose.js
query.expose({
embody: {
$filter({options, filters, params}) {
filters.threadId = params.threadId;
options.namespace = `thread::${params.threadId}`;
}
}
})
```
Note that `embody` can also be a function:
```js
// query.expose.js
query.expose({
embody(body, params) {
// Modify body here
}
})
```
## Conclusion
Using some simple techniques we can make our code much easier to read, and we can make use of a scalable data graph using `redis-oplog`
## [Continue Reading](outside_meteor.md) or [Back to Table of Contents](index.md)