Write simple, maintainable Nodejs

@fantasyni from Pomelo Netease

About me

Category

Overview -- What is Node.js

Overview -- What is Node.js

Overview -- What is Node.js

http://modulecounts.com/

Overview -- Node.js in the industry

PayPal measured a 2x increase in developer productivity and high performance (2x)

Overview -- Node.js in the industry

Pomelo industry

Overview -- Node.js in the industry

Goal: Simple and Maintainable

Overview -- Node.js development

require exports is not good enough

it seems to be nothing serious... however when you require a lot...

Overview -- Node.js development

require exports is not good enough

With simple require and exports, caller and callee are tightly coupled, developers have to deal with it everytime

Overview -- Node.js development

Consistent development

Node is quite flexible, developers can write several styles of code, like callback, promise, generator with koa, function programming, OOP programming etc...

app.get('/', function(req, res){
  res.send('Hello World');
});
Q.fcall(promisedStep1)
.then(promisedStep2)
.done();
var koa = require('koa');
var app = koa();

app.use(function *(){
  this.body = 'Hello World';
});

app.listen(3000);

Inconsistent code stypes, makes it hard to maintain especially in large team work projects

Overview -- Node.js development

Consistent configuration

var serverConfig = require('../../config/server');
var redis = require("redis");
var client = redis.createClient(serverConfig['redisPort'], serverConfig['redisHost']);

client.on("error", function(err) {
  console.error("redis error " + err);
});

client.on("ready", function() {
  console.log("redis is ready");
});

module.exports = client;

you require a config file, and set the redis connection

however, when you switch environments which means serverConfig may be changed, you can not do well with it

Overview -- Node.js development

How to avoid these ?

How to make node.js development simple and maintainable ?

Solution: framework

Framework -- Bearcat

Bearcat is a POJOs based application framework which provides a lightweight container for writing simple, maintainable node.js, it is designed to solve all of these painful things.

Simple POJOs + Configuration metadatas = Elastic, maintainable system

Framework -- Bearcat

the main concept of Bearcat is:

Framework -- Bearcat -- POJO

What is POJO ?

var POJO = function() {
    this.props = null;
}

POJO.prototype.method = function() {

}

module.exports = POJO;

Why should we use POJO ?

Framework -- Bearcat -- IoC

Framework -- Bearcat -- IoC

Without IoC

var Engine = require('./engine');
var Wheel = require('./wheel');

var Car = function() {
  this.engine = new Engine();
  this.wheel = new Wheel();
}

Car.prototype.run = function() {
  this.engine.run();
  var res = this.wheel.run();
  console.log('run car...');
  return 'car ' + res;
}

module.exports = Car;

car have to require engine and wheel, and then instantiate with constructor functions, it is tightly coupled

Framework -- Bearcat -- IoC

With IoC

var Car = function($engine) {
  this.$id = "car";
  this.$engine = $engine;
  this.$wheel = null;
}

Car.prototype.run = function() {
  this.$engine.run();
  var res = this.$wheel.run();
  console.log('run car...');
  return 'car ' + res;
}

module.exports = Car;

car do not have to know where engine, wheel are from, and how they are being instantiated

Framework -- Bearcat -- IoC

How to make IoC work ?

just add a simple configuration metadata file context.json

{
  "name": "simple_inject",
  "scan": ""
}

Framework -- Bearcat -- IoC

Bearcat Startup

var Bearcat = require('bearcat');
var contextPath = require.resolve('./context.json');

var bearcat = Bearcat.createApp([contextPath]);
bearcat.start(function(){
   var car = bearcat.getBean('car'); // get bean
   car.run(); // call the method
});
[2014-05-04 18:50:41.996] [INFO] bearcat - [app] Bearcat startup in 6 ms
run engine...
run wheel...
run car...

Framework -- Bearcat -- IoC

More magic in Bearcat IoC

Framework -- Bearcat -- IoC

More magic in Bearcat IoC

Framework -- Bearcat -- IoC

More magic in Bearcat IoC

Framework -- Bearcat -- IoC

More magic in Bearcat IoC

Framework -- Bearcat -- IoC

More magic in Bearcat IoC

Framework -- Bearcat -- IoC

More magic in Bearcat IoC

Framework -- Bearcat -- IoC

More details abount Bearcat IoC

Framework -- Bearcat -- AOP

Aspect-Oriented Programming (AOP) -- key unit -- aspect.

Object-Oriented Programming (OOP) -- key unit -- class.

Aspects enable the modularization of concerns such as transaction management that cut across multiple types and objects.

var Car = function() {
}

Car.prototype.run = function() {
  // pointcut
  // advice code ...
  console.log('Car run...');
  // pointcut
  // advice code ...
}

Framework -- Bearcat -- AOP

aspect -- log -- without AOP

var Car = function(engine) {
  this.engine = engine;
  this.wheel = null;
  this.log = null;
}

Car.prototype.run = function() {
  this.log.info('log...');
  this.engine.run();
  var res = this.wheel.run();
  console.log('run car...');
  return 'car ' + res;
}

module.exports = Car;

Framework -- Bearcat -- AOP

aspect -- log -- without AOP

var Engine = function() {
    this.log = null;
}

Engine.prototype.run = function() {
  this.log.info('log...');
  console.log('run engine...');
  return 'engine';
}

module.exports = Engine;

Framework -- Bearcat -- AOP

aspect -- log -- without AOP

var Wheel = function() {
    this.log = null;
}

Wheel.prototype.run = function() {
  this.log.info('log...');
  console.log('run wheel...');
  return 'wheel';
}

module.exports = Wheel;

Framework -- Bearcat -- AOP

aspect -- log -- with AOP

Framework -- Bearcat -- AOP

aspect -- log -- with AOP

[2014-05-04 18:50:41.996] [INFO] bearcat - [app] Bearcat startup in 6 ms
log...
run engine...
log...
run wheel...
log...
run car...

Framework -- Bearcat -- AOP

aspect -- transaction -- with AOP

Transaction management is a good example of a AOP crosscutting concern in enterprise Node.js applications

node-mysql provides simple transaction support at the connection level:

connection.beginTransaction(function(err) {
  connection.query('INSERT INTO posts SET title=?', title, function(err, result) {
    if (err) { 
      return connection.rollback(function() {});
    }
    var log = 'Post ' + result.insertId + ' added';
    connection.query('INSERT INTO log SET data=?', log, function(err, result) {
      if (err) { 
        return connection.rollback(function() {});
      }  
      connection.commit(function(err) {
        if (err) { 
          return connection.rollback(function() {});
        }
        console.log('success!');
      });
    });
  });
});

Framework -- Bearcat -- AOP

aspect -- transaction -- with AOP

with Bearcat AOP, transaction will be simpler

SimpleService.prototype.testMethodTransaction = function(cb, txStatus) {
    var self = this;
    this.simpleDao.transaction(txStatus).addPerson(['aaa'], function(err, results) {
        if (err) {
            return cb(err); // if err occur, rollback will be emited
        }
        self.simpleDao.transaction(txStatus).getList([1, 2], function(err, results) {
            if (err) { 
                return cb(err); // if err occur, rollback will be emited
            }
            cb(null, results); // commit the operations
        });
    });
}

more details can be refered to bearcat-dao transaction

Framework -- Bearcat -- Consistent configuration

Configurations are actually properties in POJO, therefore, you can use DI to solve it

var Car = function() {
  this.$id = "car";
  this.$Vnum = "${car.num}";
}

Car.prototype.run = function() {
  console.log('run car' + this.$Vnum);
  return 'car' + this.$Vnum;
}

module.exports = Car;

Framework -- Bearcat -- Consistent configuration

Framework -- Bearcat -- Consistent configuration

Framework -- Bearcat -- Consistent configuration

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Practice

Framework -- Bearcat -- Conclusion

Bearcat -- lightweight container

Framework -- Bearcat -- Conclusion

Brings to node

Framework -- Bearcat -- Conclusion

Simple POJOs + Configuration metadatas = Elastic, maintainable system

QA

/

#