The Flows

Explaining how things are related to each others.

Overview

Before an Idylle application starts, it loads components in a specific order.

Illustration describing the full setup of an Idylle Application.

For each step, if a promise is returned, idylle will wait the Promise to be resolve to continue. If a Promise is rejected during the init process. The application won't start.

Going with the flow

By default Idylle will require recursively all files in specific directories for each step.

project_directory/
    | actions/      <------- domain related functions, controllers, ...
    | boot/         <------- database connections, fixtures, ...
    | cache/        <------- cache system definition/initialization
    | middlewares/  <------- generic middleware layers (body-parser, loggers, cors, mutler, ...)
    | models/       <------- ORM/ODM models configuration
    | routes/       <------- HTTP routing configuration
    | settings/     <------- server configuration (port, tokens, secrets, hosts, ...)

It means you dont have anything to do except to put your files at the right place. Idylle will load each of them automatically.

As an example, an action with the path actions/users/create.js will be available through app.actions.users.create()

Customizing the flow

For each step of the flow, you can subscribe to events to customize the behavior.

Note that you can subscribe multiple time to the same event if you want to split responsibilities.

1. The dependencies

const Core = require('idylle').Core;
const app = new Core();

app
    .on(Core.events.init.dependencies, () => ({ 
        errorHandler: require('./myErrorHandler'),
        responseHandler: require('./myResponseHandler'),
        criteriaBuilder: require('./myCriteriaBuilder')
    }))
    .start();

module.exports = app;

Each of these dependencies will be used during the lifecycle of the application. To understand the responsibility of each of these components check CriteriaBuilder, ResponseHandler, ErrorHandler documentation.

2. The settings

const Core = require('idylle').Core;
const app = new Core();

app
    .on(Core.events.init.settings, settings => settings.port = 3000)
    .on(Core.events.init.settings, settings => settings.secret = 's3cr3t')
    .start();

module.exports = app;

When it comes to configure the server, you need to listen on a specific event Core.events.init.settings.

3. The models

const Core = require('idylle').Core;
const app = new Core();
const mongoose = require('mongoose');

app
    .on(Core.events.init.models, app => {
        app.models = app.models || { noSQL : {} };
        app.models.noSQL.User = mongoose.model('User', { login: String, password: String }) 
    })
    .start();

module.exports = app;

Loading the models is associated to Core.events.init.models.

4. The middlewares

const Core = require('idylle').Core;
const app = new Core();
const bodyParser = require('bodyParser');

app
    .on(Core.events.init.middlewares, app => {
        app.middlewares = app.middlewares || {};
        app.middlewares.bodyParser = bodyParser
    })
    .start();

module.exports = app;

Loading the middlewares is associated to Core.events.init.middlewares.

5. The Actions

const Core = require('idylle').Core;
const app = new Core();

app
    .on(Core.events.init.actions, app => {
        app.actions = app.actions || {};
        app.actions.users = require('./actions/users')(app);
    })
    .start();

module.exports = app;

Loading the actions is associated to Core.events.init.actions.

6. The Routes

const Core = require('idylle').Core;
const app = new Core();

app
    .on(Core.events.init.routes, app => {
        const router = app.Router();
        router
            .get('/',     app.actions.users.list.expose())
            .post('/',    
                    app.middlewares.bodyParser.json(),
                    app.actions.users.create.expose());
        app.router.use(`/api/users`, router);
    })
    .start();

module.exports = app;

Loading the routes is associated to Core.events.init.routes.

7. The Boot

const Core = require('idylle').Core;
const app = new Core();
const mongoose = require('mongoose');

app
    .on(Core.events.booting, app => {
        mongoose.connect(app.settings.db.uri);
    })
    .start();

module.exports = app;

Booting phase is associated to Core.events.booting.

8. The Post Start

const Core = require('idylle').Core;
const app = new Core();

app
    .on(Core.events.init.dependencies, () => ({ errorHandler: require('./errors') }))
    .on(Core.events.started, app => console.log(`API listening on port ${app.settings.port}`))
    .start();

module.exports = app;

Post start phase is associated to Core.events.started.

Conclusion

As you can see it is very easy to customize the initialization of an Idylle application. Most of the time you will keep the default behavior, but since Idylle will require all the files in specific paths from the Node Working Directory (CWD), when you build a library with Idylle, you might want to explicitly load directories.

Examples : Yemma, Chappai

Last updated