-
Notifications
You must be signed in to change notification settings - Fork 17
Cross cutting concerns implementation with AOP
AOP stands for Aspect Oriented Programmation. It's a strategy of changing code behaviour in a post-processing step of the code build (like compilation or post-compilation for a compiled language).
The injected behaviour (for instance logging) is called an 'advice'. The injected point (where in the code the behaviour is injected) is called a 'pointcut'. The action of injecting the advice at a pointcut is called 'weaving'.
In compiled languages, weaving requires specific tools and time-consuming processes in the code build.
On the contrary, Javascript is a dynamically typed language which natively allows code change at runtime.
AOP can be done manually in Javascript, but libraries exists to help us :
We will use Backbone.Advice in the following example, which is dedicated to Backbone AOP.
In this example, we will :
- Obtain the modules list
- Select the view modules
- Add logging around
initialize
andrender
methods of Backbone.View.
See this wiki page to understand how to obtains the CommonJS modules list of our SPA.
In the init method of our SPA, we give the modules list to a specific aspects module which will handle the weaving.
The weaving needs to be executed before the application.initialize()
call.
var aspects = require('./aspects');
var initialize = {
init: function init(moduleList) {
$(function () {
/* Weaving */
aspects.weave(moduleList);
/* ... */
/* Initialize application. */
application.initialize();
});
}
};
module.exports = initialize;
This is the aspects
modules of the SPA :
/* Logger */
var logView = require('../loggers/Spa.View');
/* Add the mixin capability (may already done) */
Backbone.Advice.addMixin(Backbone.View);
/* Log advice factory. */
function createLogAdvice(options, funcName) {
return function (orig) {
/* Start watch */
var start = new Date();
/* Execute target function */
var result = orig();
/* Stop watch */
var end = new Date();
var timespanMs = end - start;
/* Log function execution */
options.log.debug(funcName + " " + options.moduleName + ' ' + timespanMs);
/* Return function result. */
return result;
};
}
/* View aspect */
var viewAspect = function (options) {
this.around('initialize', createLogAdvice(options, 'initialize'));
this.around('render', createLogAdvice(options, 'render'));
};
/* Define whether a module is a view. */
function isViewModule(moduleName) {
/* The implementation depends of the module hierarchy and name convention in the SPA. */
}
var aspects = {
/**
* Weaves the advices on the commonJS module list.
* @param moduleList CommonJS module name list.
*/
weave: function (moduleList) {
/* View weaving */
_.each(moduleList, function (moduleName) {
if (isViewModule(moduleName)) {
/* Load module. */
var moduleContent = require('../' + moduleName);
/* Weave module*/
moduleContent.mixin([
viewAspect
], {
log: logView,
moduleName: moduleName
});
}
});
}
}
module.exports = aspects;
Using a isViewModule
method, we determine for a given module name if its a view.
We use an around
advice, which acts like a decorator pattern, to mesure the execution time of a method, and then log the name of the method and the execution time.
The advice is used on the initialize
and render
method of the view modules.
Chrome console on a message search list :
08:14:35:531 Spa.Application DEBUG initialize views/message/messageRecherche/messageResults 0
08:14:35:539 Spa.Application DEBUG initialize views/message/messageRecherche/messageCriteria 10
08:14:35:555 Spa.Application DEBUG render views/message/messageRecherche/messageResults 1
08:14:35:557 Spa.Application DEBUG render views/message/messageRecherche/messageCriteria 14
08:14:35:565 Spa.Application DEBUG render views/message/messageRecherche/messageResults 1
08:14:35:566 Spa.Application DEBUG render views/message/messageRecherche/messageCriteria 5
08:14:35:578 Spa.Application DEBUG render views/message/messageRecherche/messageResults 4
In a few lines of code, we manage to inject generic logging behaviour from the outside of the modules. AOP can be used also for models, services, etc.