Node.js Architecture

Domains

  • We structure code into domains (e.g. users, posts).
  • Each domain has it’s own folder.
  • Domain folder has subfolders split by technology: models, schema, views, interactors, utils, router, etc.
  • Domain has index.js file, which exposes domain’s interface. This allows us to easily change the internal structure of the domain folder without breaking other domains which depend on it.
  • Domain subfolders should have index.js which exports modules used outside of that subfolder. This way subfolder structure can change, but imports in other files of the same domain stay the same. In domain index.js use export { ... } from './subfolder'.
  • Do not use relative paths when importing another domain, use import '~/domain' instead. This prevents imports from breaking if you need to move a file into another subfolder.
  • Domain names should be in camel-case.

Sequelize

  • Define model in the domain and add it to global models.js to ensure it is loaded properly.
  • Import models from ‘~/models’, not directly from domains, this ensures model associations are loaded correctly.
  • Define associations in associate method, to prevent circular imports.

GraphQL

  • Domain’s graphql schema is split into queryFields.js, mutationFields.js and types.js to prevent bloated files.
  • For non-trivial resolvers use queries and interactors, which can be easily tested.
  • Use sequelizeConnection from graphql-sequelize to define connections. Those connections will use Dataloader under the hood to efficiently load data. Use connectionDefinitions from graphql-relay only when it is necessary, for example, there is no table for that connection in the database.
  • Use custom circularConnection helper to define circular connections (eg. article has many similar questions and a question has many similar articles). This prevents the issue of not defined graphql types when using circular imports.