suissa/arquitetura-foda

语言: JavaScript

git: https://github.com/suissa/arquitetura-foda

一个balaio de gato
Um balaio de gato
README.md (中文)

原子设计通用

运行混合模块的体系结构,如服务(谁知道使用Docker),用不同语言编写和使用不同的库,所有这些都是异步的,基于事件或REST。

一个标准,以便可以在模块中重用这些函数,这些函数是异步的,并且具有REST API和/或事件。

[解释那个狗屎更好,Caraleo]

使用已经创建为JavaScript模块的函数创建JSON模式,将来如果要更改框架或编程语言,只需要使用JSON配置模块并为该特定语言或框架运行生成器。通过创建模块如何通信的算法,任何开发人员都可以生成它想要的代码,但它将使用相同的语言而不是语言,HTTP或事件。

创建一个由原子函数组成的模块模式,这些函数可以在模块中重用,这些模块是异步的,并且具有REST API和/或事件。

前端

  • 原子设计无功
  • StyleGuide驱动开发
  • 前端驱动开发
  • 移动先行
  • 离线一

后端

  • API-第一
  • 功能反应式编程
  • 事件驱动
  • 同构
  • RabbitMQ的? ZeroMQ?剧团
  • UniversalValidator
  • 服务人员?
  • 搬运工人
  • runtime.js

骰子银行

  • DALU:数据访问层通用
  • 用于访问数据库的层,最初用于NoSQL,其中所有库都具有相同的CRUD API,但每种类型的数据库都具有特定的功能,例如: 图表:搜索我们 缓存:数据到期 等等 此外,它还会收到一个JSON作为放置数据的路线图,例如:

在MongoDB中插入对象

使用_id和name创建一个对象并插入到Neo4J中

缓存中的更新

架构

模块即服务

每个模块在应用程序级别必须是原子的且独立的,因为它的内部操作对系统无关紧要,应用程序不应事先知道其模块。

作为服务运行的模块需要提供2个API:

  • 休息
  • 活动

休息

活动

功能

基于原子函数的体系结构和具有配置JSON的模块,必须为所需的语言和框架读取和生成该模块。

在这个架构中,我们将有一个消息传递系统,它将成为每个人的EventEmitter,但在Node.js中我们可以使用EventEmitter。

Express中路由的JSON示例,但可用于在Angular或任何其他路由中生成路由。

var express = require('express')
  , router = express.Router()
  , Controller = require('./../controller')
  , Routes = require('./../../routes')
  , EventController = require('event-controller')
  ;

var cbCreate = function(req, res) {
    var data = req.body;
    EventController.emit('MyModuleCreate', data);
  }
  , cbRetrieve = function(req, res) {
    EventController.emit('MyModuleRetrieve');
  }
  , cbGet = function(req, res) {
    var find = res.body.find;
    EventController.emit('MyModuleGet', find);
  }
  , cbUpdate = function(req, res) {
    var data = req.body;
    EventController.emit('MyModuleUpdate', data);
  }
  , cbDelete = function(req, res) {
    var find = res.body.find;
    EventController.emit('MyModulDelete', find);
  }
  ;

var routes = [{
      action: 'create'
    , method: 'post'
    , url: '/'
    , callback: cbCreate
    }
  , {
      action: 'retrieve'
    , method: 'get'
    , url: '/'
    , callback: cbRetrieve
  }
  , {
      action: 'get'
    , method: 'get'
    , url: '/:id'
    , callback: cbGet
  }
  , {
      action: 'update'
    , method: 'put'
    , url: '/:id'
    , callback: cbUpdate
  }
  , {
      action: 'delete'
    , method: 'delete'
    , url: '/:id'
    , callback: cbDelete
  }
];

Routes.createModuleRoutes(router, routes);

module.exports = router;

现在重构使用事件。

var express = require('express')
  , router = express.Router()
  , Controller = require('./../controller')
  , Routes = require('./../../routes')
  , EventController = require('event-controller')
  ;

var cbCreate = function(req, res) {
    var data = req.body;
    EventController.emit('MyModuleCreate', data);
    Controller.create(req, res);
  }
  , cbRetrieve = function(req, res) {
    EventController.emit('MyModuleRetrieve');
      Controller.retrieve(req, res);
  }
  , cbGet = function(req, res) {
    var find = res.body.find;
    EventController.emit('MyModuleGet', find);
      Controller.get(req, res);
  }
  , cbUpdate = function(req, res) {
    var data = req.body;
    EventController.emit('MyModuleUpdate', data);
      Controller.update(req, res);
  }
  , cbDelete = function(req, res) {
    var find = res.body.find;
    EventController.emit('MyModulDelete', find);
      Controller.remove(req, res);
  }
  ;

var routes = [{
      action: 'create'
    , method: 'post'
    , url: '/'
    , callback: cbCreate
    }
  , {
      action: 'retrieve'
    , method: 'get'
    , url: '/'
    , callback: cbRetrieve
  }
  , {
      action: 'get'
    , method: 'get'
    , url: '/:id'
    , callback: cbGet
  }
  , {
      action: 'update'
    , method: 'put'
    , url: '/:id'
    , callback: cbUpdate
  }
  , {
      action: 'delete'
    , method: 'delete'
    , url: '/:id'
    , callback: cbDelete
  }
];

Routes.createModuleRoutes(router, routes);

module.exports = router;

分析这段代码,我们注意到了路由数组的模式。

路线 - 标准

var routes = [{
      action: 'create'
    , method: 'post'
    , url: '/'
    , callback: cbCreate
    }
  , {
      action: 'retrieve'
    , method: 'get'
    , url: '/'
    , callback: cbRetrieve
  }
  , {
      action: 'get'
    , method: 'get'
    , url: '/:id'
    , callback: cbGet
  }
  , {
      action: 'update'
    , method: 'put'
    , url: '/:id'
    , callback: cbUpdate
  }
  , {
      action: 'delete'
    , method: 'delete'
    , url: '/:id'
    , callback: cbDelete
  }
];

请注意,路由中的每个对象都是:

{
    action: 'create'
  , method: 'post'
  , url: '/'
  , callback: cbCreate
}

那是:

{
    ação
  , métodoHTTP
  , url
  , funçãoASerExecutada
}

然后我们可以标准化为:

Route.action
Route.method
Route.url
Route.callback

在这种情况下,我们将路径创建为可以重用的原子,我们可以像这样模块化它:

// route.create.js
const ACTION = 'create';
const METHOD = 'post';
const URL = '/';
const CALLBACK = function(req, res) {
    var data = req.body;
    Controller.create(req, res);
}

var Route = {
    action: ACTION
  , method: METHOD
  , url: URL
  , callback: CALLBACK
};
module.exports = Route

Agra意识到我们仍然对这个模块有Controller依赖,所以如果我们想要使用另一个外部模块,我们需要在它的路由上注入它。

// route.create.js
var Route = function(Action) {
const ACTION = 'create';
const METHOD = 'post';
const URL = '/';
const CALLBACK = function(req, res) {
    var data = req.body;
    Action.create(req, res);
}
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;

我们使用const来确保我们的路由是一个“纯模块”,Pure Functions的概念,在这种情况下使用这个创建路由模式我们只需要通过注入包含回调函数的Action对象来导入它。

我们已经可以在Node v4中使用let和const。

扩展ES6支持

ECMA-262是JavaScript语言规范的最新版本 - 通过构建最新的V8版本 - Node.js 4.0.0支持许多开箱即用的新语言功能。 这里有些例子 使用let和const阻止作用域 类。是的,JavaScript现在支持类,但它们只是围绕JavaScript的原型继承模型构建的语法糖。因此,如果您正在使用CoffeeScript,因为如果没有“class”关键字,您就无法生存,这个新功能适合您。 生成器(函数*,下一个和良率)习惯并理解它们是有意义的。它们将来会被大量使用。例如,koa.js是由快递人员建造的,并且在很大程度上取决于他们。

来源:http://apmblog.dynatrace.com/2015/09/05/all-you-need-to-know-about-node-js-4-0/

好吧,如果我们现在已经拥有创建路由的默认值,那么其余的CRUD很容易:

  • 检索:全部列出
// route.retrieve.js
var Route = function(Action) {
const ACTION = 'retrieve';
const METHOD = 'get';
const URL = '/';
const CALLBACK = function(req, res) {
    var data = req.body;
    Action.retrieve(req, res);
}
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;
  • 更新:从路径中的变量:id的唯一标识符更改实体:
// route.update.js
var Route = function(Action) {
const ACTION = 'update';
const METHOD = 'put';
const URL = '/:id';
const CALLBACK = function(req, res) {
    var data = req.body;
    Action.update(req, res);
}
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;
  • 删除:从唯一标识符中删除实体,该唯一标识符是路径中的变量:id:
// route.delete.js
var Route = function(Action) {
const ACTION = 'delete';
const METHOD = 'delete';
const URL = '/:id';
const CALLBACK = function(req, res) {
    var data = req.body;
    Action.delete(req, res);
}
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;

你有没有注意到另一个标准???

那么看看Action函数的调用,函数名总是ACTION常量的值,所以让我们将它重构为:

// route.default.js
const Route = function(Action, RouteConfig) {
const ACTION = 'delete';
const METHOD = 'delete';
const URL = '/:id';
const CALLBACK = function(req, res) {
    var data = req.body;
    Action.ACTION(req, res);
}
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;

我们的路由配置对象如下所示:

RouteConfig.action;
RouteConfig.method;
RouteConfig.url;
RouteConfig.callback;
};
module.exports = RouteConfig

所以我可以在Routes模块中这样做:

// route.create.config.js
const RouteConfig = {
    action: 'create'
  , method: 'post'
  , url: '/'
  , callback: ''
};
module.exports = RouteConfig;

因此,要创建一个创建路径,我们使用:

const ACTIONS_FOLDER = './actions/';
const ROUTES_FOLDER = './routes/';

const Action = require(ACTIONS_FOLDER + 'action.create.js');
const RouteConfig = require(ROUTES_FOLDER + 'route.create.config');
const RouteCreate = require(ROUTES_FOLDER + 'route.default.js')(Action, RouteConfig);

console.log(RouteCreate.action);

创建其他路线:

const ACTIONS_FOLDER = './actions/';
const ROUTES_FOLDER = './routes/';

// Create
const Action = require(ACTIONS_FOLDER + 'action.create.js');
const RouteConfig = require(ROUTES_FOLDER + 'route.create.config');
const RouteCreate = require(ROUTES_FOLDER + 'route.default.js')(Action, RouteConfig);
// Retrieve
const Action = require(ACTIONS_FOLDER + 'action.retrieve.js');
const RouteConfig = require(ROUTES_FOLDER + 'route.retrieve.config');
const RouteCreate = require(ROUTES_FOLDER + 'route.default.js')(Action, RouteConfig);
// Update
const Action = require(ACTIONS_FOLDER + 'action.update.js');
const RouteConfig = require(ROUTES_FOLDER + 'route.update.config');
const RouteCreate = require(ROUTES_FOLDER + 'route.default.js')(Action, RouteConfig);
// Delete
const Action = require(ACTIONS_FOLDER + 'action.delete.js');
const RouteConfig = require(ROUTES_FOLDER + 'route.delete.config');
const RouteCreate = require(ROUTES_FOLDER + 'route.default.js')(Action, RouteConfig);

你注意到另一种模式吗?

然后看看这个重构!

没有拍卖没有[安排口音]

除了操作之外,routes模块还将其设置作为actions和routes文件夹,因此:

// routes.js
const ACTIONS_FOLDER = './actions/';
const ROUTES_FOLDER = './routes/';
const ACTIONS = ['create', 'retrieve', 'update', 'delete'];
var Routes = [];

ACTIONS.forEach(function(action) {
  const Action = require(ACTIONS_FOLDER + 'action.'+ action +'.js');
  const Config = require(ROUTES_FOLDER + 'route.'+ action +'.config');
  const Route = require(ROUTES_FOLDER + 'route.default.js')(Action, Config);
  Routes.push(Route);
});
module.exports = Routes;

我们的路线模式如下:

// route.default.js
const Route = function(Action, RouteConfig) {
  const ACTION = RouteConfig.action;
  const METHOD = RouteConfig.method;
  const URL = RouteConfig.url;
  const CALLBACK = function(req, res) {
    Action.ACTION(req, res);
  }
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;

每条路由的配置文件是:

// route.create.config.js
const RouteConfig = {
    action: 'create'
  , method: 'post'
  , url: '/'
  , callback: ''
};
module.exports = RouteConfig;

// route.retrieve.config.js
const RouteConfig = {
    action: 'retrieve'
  , method: 'get'
  , url: '/'
  , callback: ''
};
module.exports = RouteConfig;

// route.update.config.js
const RouteConfig = {
    action: 'update'
  , method: 'update'
  , url: '/:id'
  , callback: ''
};
module.exports = RouteConfig;

// route.delete.config.js
const RouteConfig = {
    action: 'delete'
  , method: 'delete'
  , url: '/:id'
  , callback: ''
};
module.exports = RouteConfig;

在这种情况下,我有一个标准的CRUD,并且很容易添加另一条路线。

操作

Action是一个独立的模块,它将执行一个功能,在这种情况下由路由上的请求启动。

让我们继续Express的例子:

const Action = {
  create: function(req, res) {
    console.log('Action CREATE');
  }
}
module.exports = Action;

让我们假设我们将使用Mongoose创建一个实体:

const Action = function(Model) {
  return {
    create: function(req, res) {
      const data = req.body;
      Model.create(data, callback);
    }
  }
};
module.exports = Action;

然后我们注入模型并调用create函数,但它需要一个回调,因为正是这个Action负责系统中数据的延续,因为Model不能承担这个责任,它只允许与Bank进行交互。

// action.create.js
const Action = function(Model) {
  const callbackJSON = function(req, res) {
    res.json(data);
  };
  return {
    create: function(req, res) {
      const data = req.body;
      Model.create(data, callbackJSON);
    }
  }
};
module.exports = Action;

但是,JSON回调只会在模型中调用,但在使用模型之前,必须根据最后一个模式创建它。

架构

在我的例子中,我使用的是Mongoose,所以我们需要首先,让我们举一个例子,我在我的工作坊中教导:

var Schema = mongoose.Schema
  ,  _schema = {
      name: { type: String, default: '' }
    , description: { type: String, default: '' }
    , alcohol: { type: Number, min: 0, default: '' }
    , price: { type: Number, min: 0, default: '' }
    , category: { type: String, default: ''}
    , created_at: { type: Date, default: Date.now }
  }
  , BeerSchema = new Schema(_schema)
  , Beer = mongoose.model('Beer', BeerSchema)
  ;

module.exports = Beer;

最初,我们将Schema与Model分开,以便可以重用它:

const Schema = {
  name: { type: String, default: '' }
, description: { type: String, default: '' }
, alcohol: { type: Number, min: 0, default: '' }
, price: { type: Number, min: 0, default: '' }
, category: { type: String, default: ''}
, created_at: { type: Date, default: Date.now }
}

module.exports = Schema;

在转到Model之前,我们将创建一个对象,它是任何Schema的默认对象:

const Schema = {
  field: {
    type: String
  , default: undefined
  , validate: Function
  , index: Boolean
  , required: Boolean
  }
}

module.exports = Schema;

另外,为了使每个字段成为一个独立的原子,我们将重构我们的Schema以创建一个字段骨架,以便稍后转换为特定的Mongoose Schema。

骨架

const Skeleton = [
  { field: 'name'
  , props:
    {
      type: String,
      default: ''
    }
  }
, { field: 'description'
  , props:
    {
      type: String,
      default: ''
    }
  }
, { field: 'alcohol'
  , props:
    {
      type: Number,
      default: ''
    }
  }
, { field: 'price'
  , props:
    {
      type: Number,
      default: ''
    }
  }
, { field: 'category'
  , props:
    {
      type: String,
      default: ''
    }
  }
, { field: 'created_at'
  , props:
    {
      type: Date,
      default: Date.now
    }
  }
]

module.exports = Skeleton;

Skeleton是一系列具有属性的字段,我们以这种方式创建,以便能够模块化,等待一点。让我们重构Schema来接受Skeleton,为此我们创建了一个Schemas Factory:

// schema.mongoose.factory.js
var Schema = {};
const SchemaSkeleton = function(Skeleton) {
  const createSchemaField = function(SkeletonAtom) {
    // chamar função que validará cada field se tem a interface correta
    Schema[SkeletonAtom.field] = SkeletonAtom.props;
  }
  Skeleton.forEach(createSchemaField);
  return Schema;
};

module.exports = SchemaSkeleton;

在创建了Skeleton和SchemaFactory之后,我们现在可以用这种方式创建一个特定的Schema:

// schema.beer.js
const Skeleton = require('./skeleton.beer');
const Schema = require('./schema.mongoose.factory')(Skeleton);

module.exports = Schema;
原子骨架

正如我所说,每个字段都是一个原子模块,那么你应该问自己:

“但为什么呢?”

因为我想在Frontend中重用那些相同的字段,所以这种架构非常模块化,因此可以在任何地方重复使用。

我们需要将Skeleton重构为:

const Skeleton = [
  require('./fields/field.name')
, require('./fields/field.description')
, require('./fields/field.alcohol')
, require('./fields/field.price')
, require('./fields/field.category')
, require('./fields/field.created_at')
]

module.exports = Skeleton;

为此,我创建了一个字段文件夹,其中包含Beer Schema的所有原子字段,例如name字段。

// field.name.js
const Field = {
  field: 'name'
  , props:
    {
      type: String,
      default: ''
    }
}

module.exports = Field;

模型

然后我们需要为Mongoose创建模型:

// model.mongoose.js
const Mongoose = require('mongoose');
const skeleton = require('./../schemas/schema.beer');
const Schema = new mongoose.Schema(skeleton);
const Model = mongoose.model('Beer', Schema);

module.exports = Model;

模型必须具有CRUD的默认接口:

Model.create;
Model.retrieve;
Model.update;
Model.delete;

并记住我们在行动中的签名:

Model.create(data, callbackJSON);

然后重构我们的模型:

// model.mongoose.js
const Mongoose = require('mongoose');
const skeleton = require('./../schemas/schema.beer');
const Schema = new mongoose.Schema(skeleton);
const ModelMongoose = mongoose.model('Beer', Schema);

Model = {
  create: function(data, callback) {
    ModelMongoose.save(data, callback);
  }
, retrieve: function(data, callback) {
    ModelMongoose.find(data.query, callback);
  }
, update: function(data, callback) {
    ModelMongoose.update(data.query, data.mod, callback);
  }
, delete: function(data, callback) {
    ModelMongoose.remove(data.query, callback);
  }
}

module.exports = Model;

在这种情况下,任何CRUD的模型都是通用的,只需要先前创建特定于您的系统的模型,我们就可以模块化更多,请与我联系:

const Model = function(ModelDB) {

  return {
    create: function(data, callback) {
      ModelDB.save(data, callback);
    }
  , retrieve: function(data, callback) {
      ModelDB.find(data.query, callback);
    }
  , update: function(data, callback) {
      ModelDB.update(data.query, data.mod, callback);
    }
  , delete: function(data, callback) {
      ModelDB.remove(data.query, callback);
    }
  };
}

module.exports = Model;

有了这个,我们创建了一个独立于银行的工厂模型,从而成为Mongoose模型:

// model.mongoose.js
const Mongoose = require('mongoose');
const skeleton = require('./../schemas/schema.beer');
const Schema = new mongoose.Schema(skeleton);
const ModelMongoose = mongoose.model('Beer', Schema);

Model = require('./model')(ModelMongoose);

module.exports = Model;

现在要使所有这些工作,我们需要创建银行模块。

配置 - 数据库

为了使我们的数据库连接是模块化的,我们必须分析哪些是与大多数银行的连接中要调用的基本值,快速在脑海中来:

  • 主办
  • 银行的地址
  • 示例:'mongodb:// localhost'
  • 银行服务之门
  • 示例:27017
  • 数据库
  • 要访问的数据库的名称

那么让我们开始考虑MongoDb的连接模块:

const DBConfig = {
  type: 'Document'
, name: 'MongoDb'
, host: 'mongodb://localhost/'
, database: 'arquitetura-foda-test'
, port: 27017
}

module.exports = DBConfig;

要创建与MongoDB的连接非常简单,旧代码取自Be MEAN:

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/maceio-setembro-2015');

var db = mongoose.connection;
db.on('error', function(err){
    console.log('Erro de conexao.', err);
});
db.on('open', function () {
  console.log('Conexão aberta.');
});
db.on('connected', function(err){
    console.log('Conectado');
});
db.on('disconnected', function(err){
    console.log('Desconectado');
});

所以基于这个代码,我们可以重构为:

// db.mongodb.js
const DB_CONFIG = require('./db.mongodb.config');
const DB_URL = DB_CONFIG.host + DB_CONFIG.database

const mongoose = require('mongoose');
mongoose.connect(DB_URL);

const db = mongoose.connection;
db.on('error', function(err){
    console.log('Erro de conexao.', err);
});
db.on('open', function () {
  console.log('Conexão aberta.');
});
db.on('connected', function(err){
    console.log('Conectado');
});
db.on('disconnected', function(err){
    console.log('Desconectado');
});

module.exports = db;

请注意,Mongoose返回一个连接的对象,其中包含一些我们可以听到的事件,很快我们将不得不为Bank的模块返回连接对象创建默认值,我将删除open事件,因为如果它连接我们就会知道它通过了这个州。

DBConnection.error;
DBConnection.connected;
DBConnection.disconnected;

对于没有事件的银行,它们将成为被调用的函数。

const DB_CONFIG = require('./db.mongodb.config');
const DB_URL = DB_CONFIG.host + DB_CONFIG.database

const mongoose = require('mongoose');
mongoose.connect(DB_URL);

const db = mongoose.connection;
var Connection = {
  callbacks: {
    error: function(err){
      return console.log('Erro de conexao.', err);
    }
  , connected: function(err){
      return console.log('Conectado');
    }
  , disconnected: function(err){
      return console.log('Desconectado');
    }
  }
, on: function(event, callback) {
    db.on(event, callback);
    return db;
  }
};
console.log(Connection);
Connection
  .on('error', Connection.callbacks.error)
  .on('connected', Connection.callbacks.connected)
  .on('disconnected', Connection.callbacks.disconnected);

module.exports = db;

请注意,即使Mongoose为我们提供了方法,也不会返回要链接的相同对象,因此除了每个事件的回调之外,我还创建了on方法,以保证任何连接的事件链接。如果我们有一个连接模块,我们需要将它分开:

// connection.default.js
const Connection = function(db) {

  return {
    callbacks: {
      error: function(err){
        return console.log('Erro de conexao.', err);
      }
    , connected: function(err){
        return console.log('Conectado');
      }
    , disconnected: function(err){
        return console.log('Desconectado');
      }
    }
  , on: function(event, callback) {
      db.on(event, callback);
      return db;
    }
  };
};

module.exports = Connection;

现在就准备好我们只需导入我们的连接并传递所需的Bank连接对象。

// db.mongodb.js
const DB_CONFIG = require('./db.mongodb.config');
const DB_URL = DB_CONFIG.host + DB_CONFIG.database

const mongoose = require('mongoose');
mongoose.connect(DB_URL);

const db = mongoose.connection;
const Connection = require('./connection.default')(db);

Connection
  .on('error', Connection.callbacks.error)
  .on('connected', Connection.callbacks.connected)
  .on('disconnected', Connection.callbacks.disconnected);

module.exports = db;

很快!

现在开始在Express中使用我们的Routes模块,我们需要创建express.js:

FRP - 功能反应式编程

功能反应式编程(FRP)是使用功能编程的构建块(例如映射,缩减,滤波器)的反应式编程(异步数据流编程)的编程范例。 FRP已用于编程图形用户界面(GUI),机器人和音乐,旨在通过明确建模时间来简化这些问题。

来源:https://en.wikipedia.org/wiki/Functional_reactive_programming

验证

为此,我们将创建一个通用/原子验证模块/服务。

阅读有关UniversalValidator的更多信息。

引用

我关于这些主题的文章:

  • HTTP://no妈Dev.com.比如/frontend-driven-development-com-mean-哦-atomic-design/
  • HTTP://no妈Dev.com.比如/full stack-offline-API-first/
  • HTTP://no妈Dev.com.比如/帕森斯哦-啊-帕森斯哦-Como-德森Vol ver-com-atomic-design-Mobile-first-哦-stylus/
  • HTTP://no妈Dev.com.比如/帕森斯哦-啊-帕森斯哦-Como-德森Vol ver-com-atomic-design-Mobile-first-哦-stylus-帕瑞特-2/

来自第三方

  • HTTPS://呜呜呜.臂搁哪儿都染成.com/blog/what-is-functional-reactive-programming/
  • HTTPS://恩.Wikipedia.org/wiki/functional_reactive_programming

本文使用googletrans自动翻译,仅供参考, 原文来自github.com

en_README.md

Atomic Design Universal

Uma arquitetura que rode módulos híbridos como serviço (quem sabe usando Docker), escritos em diferentes linguagens e usando diferentes bancos, todos assíncronos e baseados em eventos ou REST.

Um padrão para que as funções possam ser reusadas nos módulos, esses sendo assíncronos e com uma API REST e/ou Eventos.

[Explicar essa merda melhor, caraleo]

Criando um padrão de JSON usando as funções já criadas como módulos de JavaScript, futuramente se você quiser mudar de framework ou linguagem de programação bastaria pegar seu JSON de configuração do módulo e executar o gerador para aquela linguagem ou framework específicos. Criando um algoritmo de como os módulos de comunicam, qualquer dev pode gerar o código que bem entender, porém usará a mesma língua e não linguagem, o HTTP ou Eventos.

Criar um padrão de módulo composto por funções atômicas que possam ser reusadas nos módulos, esses sendo assíncronos e com uma API REST e/ou Eventos.

Frontend

  • Atomic Design Reativo
  • StyleGuide Driven Development
  • Frontend Driven Development
  • Mobile-first
  • Offline-first

Backend

Banco de dados

  • DALU: Data Access Layer Universal
  • Uma camada para acesso a bancos de dados, inicialmente para NoSQL onde todos os bancos terão a mesma API de CRUD, porém cada tipo de banco terá funcionalidades específicas, como:
    • Grafos: busca por nós
    • Cache: expiração de dados
    • etc.
      Além disso ele receberia um JSON como um roteiro de onde colocar os dados, por exemplo:

Insere o objeto no MongoDB

Cria um objeto com _id e nome e insere no Neo4J

Atualiza no Cache

Arquitetura

Módulos como Serviço

Cada módulo deve ser atômico e independente no nível da aplicação, pois seu funcionamento interno não importa para o sistema, a aplicação não deve conhecer de antemão seus módulos.

O módulo para funcionar como ums serviço precisa prover 2 APIs:

  • REST
  • Eventos

REST

Eventos

Funções

Arquitetura baseada em funções atômicas e um módulo com um JSON de config que deve ser lido e gerado para a linguagem e framework que quiser.

Nessa arquitetura teremos umm sistema de mensagens que será nosso EventEmitter para todos, porém no Node.js podemos usar o EventEmitter.

Exemplo de JSON de rotas no Express mas que pode ser usado para gerar rotas no Angular ou qualquer outro.

var express = require('express')
  , router = express.Router()
  , Controller = require('./../controller')
  , Routes = require('./../../routes')
  , EventController = require('event-controller')
  ;

var cbCreate = function(req, res) {
    var data = req.body;
    EventController.emit('MyModuleCreate', data);
  }
  , cbRetrieve = function(req, res) {
    EventController.emit('MyModuleRetrieve');
  }
  , cbGet = function(req, res) {
    var find = res.body.find;
    EventController.emit('MyModuleGet', find);
  }
  , cbUpdate = function(req, res) {
    var data = req.body;
    EventController.emit('MyModuleUpdate', data);
  }
  , cbDelete = function(req, res) {
    var find = res.body.find;
    EventController.emit('MyModulDelete', find);
  }
  ;

var routes = [{
      action: 'create'
    , method: 'post'
    , url: '/'
    , callback: cbCreate
    }
  , {
      action: 'retrieve'
    , method: 'get'
    , url: '/'
    , callback: cbRetrieve
  }
  , {
      action: 'get'
    , method: 'get'
    , url: '/:id'
    , callback: cbGet
  }
  , {
      action: 'update'
    , method: 'put'
    , url: '/:id'
    , callback: cbUpdate
  }
  , {
      action: 'delete'
    , method: 'delete'
    , url: '/:id'
    , callback: cbDelete
  }
];

Routes.createModuleRoutes(router, routes);

module.exports = router;

Agora refatorando para usar eventos.

var express = require('express')
  , router = express.Router()
  , Controller = require('./../controller')
  , Routes = require('./../../routes')
  , EventController = require('event-controller')
  ;

var cbCreate = function(req, res) {
    var data = req.body;
    EventController.emit('MyModuleCreate', data);
    Controller.create(req, res);
  }
  , cbRetrieve = function(req, res) {
    EventController.emit('MyModuleRetrieve');
      Controller.retrieve(req, res);
  }
  , cbGet = function(req, res) {
    var find = res.body.find;
    EventController.emit('MyModuleGet', find);
      Controller.get(req, res);
  }
  , cbUpdate = function(req, res) {
    var data = req.body;
    EventController.emit('MyModuleUpdate', data);
      Controller.update(req, res);
  }
  , cbDelete = function(req, res) {
    var find = res.body.find;
    EventController.emit('MyModulDelete', find);
      Controller.remove(req, res);
  }
  ;

var routes = [{
      action: 'create'
    , method: 'post'
    , url: '/'
    , callback: cbCreate
    }
  , {
      action: 'retrieve'
    , method: 'get'
    , url: '/'
    , callback: cbRetrieve
  }
  , {
      action: 'get'
    , method: 'get'
    , url: '/:id'
    , callback: cbGet
  }
  , {
      action: 'update'
    , method: 'put'
    , url: '/:id'
    , callback: cbUpdate
  }
  , {
      action: 'delete'
    , method: 'delete'
    , url: '/:id'
    , callback: cbDelete
  }
];

Routes.createModuleRoutes(router, routes);

module.exports = router;

Analisando esse código percebemos um padrão para o array de rotas.

Rotas - Padrão

var routes = [{
      action: 'create'
    , method: 'post'
    , url: '/'
    , callback: cbCreate
    }
  , {
      action: 'retrieve'
    , method: 'get'
    , url: '/'
    , callback: cbRetrieve
  }
  , {
      action: 'get'
    , method: 'get'
    , url: '/:id'
    , callback: cbGet
  }
  , {
      action: 'update'
    , method: 'put'
    , url: '/:id'
    , callback: cbUpdate
  }
  , {
      action: 'delete'
    , method: 'delete'
    , url: '/:id'
    , callback: cbDelete
  }
];

Perceba que cada objeto da rota é:

{
    action: 'create'
  , method: 'post'
  , url: '/'
  , callback: cbCreate
}

Ou seja:

{
    ação
  , métodoHTTP
  , url
  , funçãoASerExecutada
}

Podemos então padronizar como:

Route.action
Route.method
Route.url
Route.callback

Nesse caso criamos a rota como um átomo que possa ser reusada, podemos modularizá-la assim:

// route.create.js
const ACTION = 'create';
const METHOD = 'post';
const URL = '/';
const CALLBACK = function(req, res) {
    var data = req.body;
    Controller.create(req, res);
}

var Route = {
    action: ACTION
  , method: METHOD
  , url: URL
  , callback: CALLBACK
};
module.exports = Route

Agra perceba que ainda temos a dependência do Controller nesse módulo, então se quisermos utilizar outro módulo externo, precisamos injetar ele na nossa rota.

// route.create.js
var Route = function(Action) {
const ACTION = 'create';
const METHOD = 'post';
const URL = '/';
const CALLBACK = function(req, res) {
    var data = req.body;
    Action.create(req, res);
}
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;

Estamos usando const para garantir que nossa rota seja um "módulo puro", conceito de Pure Functions, nesse caso com esse padrão de rota para o create precisamos apenas importá-lo injetando o objeto Action que contém a função de callback.

Já podemos usar sussegadamente let e const com o Node v4.

Extended ES6 support

ECMA-262 is the latest version of the JavaScript language specification and – by building on a recent V8 version – Node.js 4.0.0 supports many new language features out of the box.
Here are some examples
Block scoping using let and const
Classes. Yes really, JavaScript now supports classes but they are just syntactic sugar built around the prototypical inheritance model of JavaScript. So if you are using CoffeeScript because you just can not live without having a ‘class’ keyword this new feature is for you.
Generators (function*, next and yield) It makes sense to get used to- and understand them. They will be heavily used in the future. koa.js, for instance, was built by the express folks and heavily depends on them.

fonte: http://apmblog.dynatrace.com/2015/09/05/all-you-need-to-know-about-node-js-4-0/

Bom se já temos o padrão para a rota create agora ficou fácil para o resto do CRUD:

  • Retrieve: lista todos
// route.retrieve.js
var Route = function(Action) {
const ACTION = 'retrieve';
const METHOD = 'get';
const URL = '/';
const CALLBACK = function(req, res) {
    var data = req.body;
    Action.retrieve(req, res);
}
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;
  • Update: altera uma entidade apatir de um identificador único sendo a variável :id na rota:
// route.update.js
var Route = function(Action) {
const ACTION = 'update';
const METHOD = 'put';
const URL = '/:id';
const CALLBACK = function(req, res) {
    var data = req.body;
    Action.update(req, res);
}
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;
  • Delete: deleta uma entidade apatir de um identificador único sendo a variável :id na rota:
// route.delete.js
var Route = function(Action) {
const ACTION = 'delete';
const METHOD = 'delete';
const URL = '/:id';
const CALLBACK = function(req, res) {
    var data = req.body;
    Action.delete(req, res);
}
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;

Notou mais um padrão???

Pois olhe bem a chamada da função do Action, o nome da função é sempre o valor da constante ACTION, então vamos refatorar para:

// route.default.js
const Route = function(Action, RouteConfig) {
const ACTION = 'delete';
const METHOD = 'delete';
const URL = '/:id';
const CALLBACK = function(req, res) {
    var data = req.body;
    Action.ACTION(req, res);
}
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;

E nosso objeto de configuração de rota ficará assim:

RouteConfig.action;
RouteConfig.method;
RouteConfig.url;
RouteConfig.callback;
};
module.exports = RouteConfig

Então eu posso fazer assim no meu módulo de Rotas:

// route.create.config.js
const RouteConfig = {
    action: 'create'
  , method: 'post'
  , url: '/'
  , callback: ''
};
module.exports = RouteConfig;

Então para criar uma Rota de create usamos:

const ACTIONS_FOLDER = './actions/';
const ROUTES_FOLDER = './routes/';

const Action = require(ACTIONS_FOLDER + 'action.create.js');
const RouteConfig = require(ROUTES_FOLDER + 'route.create.config');
const RouteCreate = require(ROUTES_FOLDER + 'route.default.js')(Action, RouteConfig);

console.log(RouteCreate.action);

Criando as outras rotas:

const ACTIONS_FOLDER = './actions/';
const ROUTES_FOLDER = './routes/';

// Create
const Action = require(ACTIONS_FOLDER + 'action.create.js');
const RouteConfig = require(ROUTES_FOLDER + 'route.create.config');
const RouteCreate = require(ROUTES_FOLDER + 'route.default.js')(Action, RouteConfig);
// Retrieve
const Action = require(ACTIONS_FOLDER + 'action.retrieve.js');
const RouteConfig = require(ROUTES_FOLDER + 'route.retrieve.config');
const RouteCreate = require(ROUTES_FOLDER + 'route.default.js')(Action, RouteConfig);
// Update
const Action = require(ACTIONS_FOLDER + 'action.update.js');
const RouteConfig = require(ROUTES_FOLDER + 'route.update.config');
const RouteCreate = require(ROUTES_FOLDER + 'route.default.js')(Action, RouteConfig);
// Delete
const Action = require(ACTIONS_FOLDER + 'action.delete.js');
const RouteConfig = require(ROUTES_FOLDER + 'route.delete.config');
const RouteCreate = require(ROUTES_FOLDER + 'route.default.js')(Action, RouteConfig);

Percebeu outro padrão?

Então olhe essa refatoração!!!

TO SEM aCENTO no a [arrumar acentos]

O módulo de rotas tera suas configurações também como a pasta de actions e routes, além das ações, ficando assim:

// routes.js
const ACTIONS_FOLDER = './actions/';
const ROUTES_FOLDER = './routes/';
const ACTIONS = ['create', 'retrieve', 'update', 'delete'];
var Routes = [];

ACTIONS.forEach(function(action) {
  const Action = require(ACTIONS_FOLDER + 'action.'+ action +'.js');
  const Config = require(ROUTES_FOLDER + 'route.'+ action +'.config');
  const Route = require(ROUTES_FOLDER + 'route.default.js')(Action, Config);
  Routes.push(Route);
});
module.exports = Routes;

Nosso padrao de rota ficou assim:

// route.default.js
const Route = function(Action, RouteConfig) {
  const ACTION = RouteConfig.action;
  const METHOD = RouteConfig.method;
  const URL = RouteConfig.url;
  const CALLBACK = function(req, res) {
    Action.ACTION(req, res);
  }
  return {
      action: ACTION
    , method: METHOD
    , url: URL
    , callback: CALLBACK
  };
}
module.exports = Route;

E os arquivos de confirguraçao de cada rota ficou:

// route.create.config.js
const RouteConfig = {
    action: 'create'
  , method: 'post'
  , url: '/'
  , callback: ''
};
module.exports = RouteConfig;

// route.retrieve.config.js
const RouteConfig = {
    action: 'retrieve'
  , method: 'get'
  , url: '/'
  , callback: ''
};
module.exports = RouteConfig;

// route.update.config.js
const RouteConfig = {
    action: 'update'
  , method: 'update'
  , url: '/:id'
  , callback: ''
};
module.exports = RouteConfig;

// route.delete.config.js
const RouteConfig = {
    action: 'delete'
  , method: 'delete'
  , url: '/:id'
  , callback: ''
};
module.exports = RouteConfig;

Nesse caso tenho um CRUD padronizado e simples de adicionar outra rota.

Actions

Uma Action é um módulo independente que executara uma funçao, nesse caso iniciado pela requisiçao em uma rota.

Vamos continuar com o exemplo do Express:

const Action = {
  create: function(req, res) {
    console.log('Action CREATE');
  }
}
module.exports = Action;

Vamos imaginar que iremos criar uma entidade com o Mongoose:

const Action = function(Model) {
  return {
    create: function(req, res) {
      const data = req.body;
      Model.create(data, callback);
    }
  }
};
module.exports = Action;

Entao injetamos o Model e chamamos a funçao create, porém ela precisa de um callback, pois é essa Action que responsavel pela continuaçao dos dados no Sistema, ja que um Model nao pode ter essa responsabilidade, ele dera apenas fazer a interaçao com o Banco.

// action.create.js
const Action = function(Model) {
  const callbackJSON = function(req, res) {
    res.json(data);
  };
  return {
    create: function(req, res) {
      const data = req.body;
      Model.create(data, callbackJSON);
    }
  }
};
module.exports = Action;

Porém o callbackJSON só sera chamado no Model, porém antes de usarmos um Model ele deve ser criado com base no Schema passado.

Schema

Como no meu exemplo estou usando Mongoose precisamos entao antes de tudo, vamos pegar um exemplo que ensino no meu Workshop Be MEaN antigo:

var Schema = mongoose.Schema
  ,  _schema = {
      name: { type: String, default: '' }
    , description: { type: String, default: '' }
    , alcohol: { type: Number, min: 0, default: '' }
    , price: { type: Number, min: 0, default: '' }
    , category: { type: String, default: ''}
    , created_at: { type: Date, default: Date.now }
  }
  , BeerSchema = new Schema(_schema)
  , Beer = mongoose.model('Beer', BeerSchema)
  ;

module.exports = Beer;

Inicialmente separamos o Schema do Model para ele poder ser reaproveitado:

const Schema = {
  name: { type: String, default: '' }
, description: { type: String, default: '' }
, alcohol: { type: Number, min: 0, default: '' }
, price: { type: Number, min: 0, default: '' }
, category: { type: String, default: ''}
, created_at: { type: Date, default: Date.now }
}

module.exports = Schema;

Antes de ir para o Model vamos criar um objeto que seja o padrão para qualquer Schema:

const Schema = {
  field: {
    type: String
  , default: undefined
  , validate: Function
  , index: Boolean
  , required: Boolean
  }
}

module.exports = Schema;

Além disso para que cada campo seja um átomo independente vamos refatorar nosso Schema para criar um esqueleto dos campos para posteriormente ser convertido no Schema específico do Mongoose.

Skeleton

const Skeleton = [
  { field: 'name'
  , props:
    {
      type: String,
      default: ''
    }
  }
, { field: 'description'
  , props:
    {
      type: String,
      default: ''
    }
  }
, { field: 'alcohol'
  , props:
    {
      type: Number,
      default: ''
    }
  }
, { field: 'price'
  , props:
    {
      type: Number,
      default: ''
    }
  }
, { field: 'category'
  , props:
    {
      type: String,
      default: ''
    }
  }
, { field: 'created_at'
  , props:
    {
      type: Date,
      default: Date.now
    }
  }
]

module.exports = Skeleton;

O Skeleton é um array de campos com suas propriedades, criamos dessa forma para poder modularizar ainda mais, aguarde um pouco. Vamos refatorar o Schema para aceitar o Skeleton, para isso criamos uma Factory de Schemas:

// schema.mongoose.factory.js
var Schema = {};
const SchemaSkeleton = function(Skeleton) {
  const createSchemaField = function(SkeletonAtom) {
    // chamar função que validará cada field se tem a interface correta
    Schema[SkeletonAtom.field] = SkeletonAtom.props;
  }
  Skeleton.forEach(createSchemaField);
  return Schema;
};

module.exports = SchemaSkeleton;

Depois de criado o Skeleton e o SchemaFactory podemos agora criar um Schema específico dessa forma:

// schema.beer.js
const Skeleton = require('./skeleton.beer');
const Schema = require('./schema.mongoose.factory')(Skeleton);

module.exports = Schema;
Skeleton Atômico

Como eu disse que cada campo será um módulo atômico, aí você deve se perguntar:

- Mas por quê?

Porque eu vou querer reusar esses mesmos campos no Frontend, essa arquitetura é bem modular para que possa ser reaproveitada em qualquer lugar.

precisamos refatorar o Skeleton para:

const Skeleton = [
  require('./fields/field.name')
, require('./fields/field.description')
, require('./fields/field.alcohol')
, require('./fields/field.price')
, require('./fields/field.category')
, require('./fields/field.created_at')
]

module.exports = Skeleton;

Para que isso seja possível eu criei uma pasta fields que conterá todos os campos atômicos do Schema de cervejas, por exemplo o campo name.

// field.name.js
const Field = {
  field: 'name'
  , props:
    {
      type: String,
      default: ''
    }
}

module.exports = Field;

Model

Precisamos entao criar o Model para o Mongoose:

// model.mongoose.js
const Mongoose = require('mongoose');
const skeleton = require('./../schemas/schema.beer');
const Schema = new mongoose.Schema(skeleton);
const Model = mongoose.model('Beer', Schema);

module.exports = Model;

O Model deve possuir uma interface padrao para o CRUD:

Model.create;
Model.retrieve;
Model.update;
Model.delete;

E lembrando da nossa assinatura na Action:

Model.create(data, callbackJSON);

Então refatorando o Model temos:

// model.mongoose.js
const Mongoose = require('mongoose');
const skeleton = require('./../schemas/schema.beer');
const Schema = new mongoose.Schema(skeleton);
const ModelMongoose = mongoose.model('Beer', Schema);

Model = {
  create: function(data, callback) {
    ModelMongoose.save(data, callback);
  }
, retrieve: function(data, callback) {
    ModelMongoose.find(data.query, callback);
  }
, update: function(data, callback) {
    ModelMongoose.update(data.query, data.mod, callback);
  }
, delete: function(data, callback) {
    ModelMongoose.remove(data.query, callback);
  }
}

module.exports = Model;

Nesse caso o Model para qualquer CRUD será genérico, bastando apenas a criação anterior do Model específico para seu sistema, podemos modularizar ainda mais, confira comigo:

const Model = function(ModelDB) {

  return {
    create: function(data, callback) {
      ModelDB.save(data, callback);
    }
  , retrieve: function(data, callback) {
      ModelDB.find(data.query, callback);
    }
  , update: function(data, callback) {
      ModelDB.update(data.query, data.mod, callback);
    }
  , delete: function(data, callback) {
      ModelDB.remove(data.query, callback);
    }
  };
}

module.exports = Model;

Com isso criamos uma forma de Factory para Models independente do Banco, ficando assim o Model do Mongoose:

// model.mongoose.js
const Mongoose = require('mongoose');
const skeleton = require('./../schemas/schema.beer');
const Schema = new mongoose.Schema(skeleton);
const ModelMongoose = mongoose.model('Beer', Schema);

Model = require('./model')(ModelMongoose);

module.exports = Model;

Agora para tudo isso funcionar precisamos criar o módulo do Banco.

Config - Database

Para que nossa conexão com o Banco de Dados seja modular temos que analisar quais são os valores básicos a serem chamados nas conexões com a maioria dos bancos, rapidamente na mente vêm:

  • host
  • endereço do serviço do Banco
  • exemplo: 'mongodb://localhost'
  • porta
  • porta do serviço do Banco
  • exemplo: 27017
  • database
  • nome do banco de dados a ser acessado

Então vamos iniciar pensando no módulo da conexão para o MongoDb:

const DBConfig = {
  type: 'Document'
, name: 'MongoDb'
, host: 'mongodb://localhost/'
, database: 'arquitetura-foda-test'
, port: 27017
}

module.exports = DBConfig;

Para criarmos uma conexão com o MongoDB é bem simples, código antigo retirado do Be MEAN:

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/maceio-setembro-2015');

var db = mongoose.connection;
db.on('error', function(err){
    console.log('Erro de conexao.', err);
});
db.on('open', function () {
  console.log('Conexão aberta.');
});
db.on('connected', function(err){
    console.log('Conectado');
});
db.on('disconnected', function(err){
    console.log('Desconectado');
});

Então baseando-me nesse código podemos refatorar para:

// db.mongodb.js
const DB_CONFIG = require('./db.mongodb.config');
const DB_URL = DB_CONFIG.host + DB_CONFIG.database

const mongoose = require('mongoose');
mongoose.connect(DB_URL);

const db = mongoose.connection;
db.on('error', function(err){
    console.log('Erro de conexao.', err);
});
db.on('open', function () {
  console.log('Conexão aberta.');
});
db.on('connected', function(err){
    console.log('Conectado');
});
db.on('disconnected', function(err){
    console.log('Desconectado');
});

module.exports = db;

Percebeu que o Mongoose retorna um objeto da conexão que possui alguns eventos que podemos ouvir, logo teremos que criar um padrão para o objeto de conexão a ser retornado pelos módulos de Banco, irei retirar o evento open pois se ele conectar já saberemos que passou por esse estado.

DBConnection.error;
DBConnection.connected;
DBConnection.disconnected;

Pois nos Bancos sem eventos, eles virarão funções a serem chamadas.

const DB_CONFIG = require('./db.mongodb.config');
const DB_URL = DB_CONFIG.host + DB_CONFIG.database

const mongoose = require('mongoose');
mongoose.connect(DB_URL);

const db = mongoose.connection;
var Connection = {
  callbacks: {
    error: function(err){
      return console.log('Erro de conexao.', err);
    }
  , connected: function(err){
      return console.log('Conectado');
    }
  , disconnected: function(err){
      return console.log('Desconectado');
    }
  }
, on: function(event, callback) {
    db.on(event, callback);
    return db;
  }
};
console.log(Connection);
Connection
  .on('error', Connection.callbacks.error)
  .on('connected', Connection.callbacks.connected)
  .on('disconnected', Connection.callbacks.disconnected);

module.exports = db;

Perceba que mesmo o Mongoose nos fornecer o método on ele não retorna o mesmo objeto para encadearmos, então para deixar o módulo de Conexão, além dos callbacks para cada evento também criei o método on que irá garantir o encadeamento de eventos para qualquer conexão. E se temos um módulo de conexão precisamos separá-lo:

// connection.default.js
const Connection = function(db) {

  return {
    callbacks: {
      error: function(err){
        return console.log('Erro de conexao.', err);
      }
    , connected: function(err){
        return console.log('Conectado');
      }
    , disconnected: function(err){
        return console.log('Desconectado');
      }
    }
  , on: function(event, callback) {
      db.on(event, callback);
      return db;
    }
  };
};

module.exports = Connection;

Pronto agora é só importar nossa conexão e passar o objeto de conexão do Banco desejado.

// db.mongodb.js
const DB_CONFIG = require('./db.mongodb.config');
const DB_URL = DB_CONFIG.host + DB_CONFIG.database

const mongoose = require('mongoose');
mongoose.connect(DB_URL);

const db = mongoose.connection;
const Connection = require('./connection.default')(db);

Connection
  .on('error', Connection.callbacks.error)
  .on('connected', Connection.callbacks.connected)
  .on('disconnected', Connection.callbacks.disconnected);

module.exports = db;

Pronto!

Agora para começarmos a usar nosso módulo Routes no Express precisamos criar o express.js:

FRP - Functional reactive programming

Functional reactive programming (FRP) is a programming paradigm for reactive programming (asynchronous dataflow programming) using the building blocks of functional programming (e.g. map, reduce, filter). FRP has been used for programming graphical user interfaces (GUIs), robotics, and music, aiming to simplify these problems by explicitly modeling time.

fonte: https://en.wikipedia.org/wiki/Functional_reactive_programming

Validação

Para isso criaremos um módulo/serviço de validação universal e atômica.

Leia mais sobre o UniversalValidator.

Refêrencias

Meus artigos sobre esses temas:

De terceiros