>
> But currently, it is important for me to use only standard (within the 
> meaning of established/mainstream) componentes and libraries (like e.g. 
> async) that every node programmer knows.


No one encourages you to use the-box as is. It's essentials just about 
50-100 lines of code. Each app might want to have it's own optimized 
version. But the pattern "hey I want this and that" is very common. 
Delegating tasks of getting dependencies to well known infrastructure is 
what makes code more simple and declarative. Here we see an idea of 
separating control flow from business logic in action.    

Can you please point out some more details why you don't find this code 
> clean/easy maintainable? And give me some example code for improvements?


Without concrete use case it's very hard to do. What you showed just has 
some smells for me, but that's all pretty subjective. For example using of 
async utils for sequential execution. They are adding their own noise both 
in code and runtime. I personally can tolerate up to 5 levels of nesting 
with 2 space tabs but can't remember when I had more than 3 levels. At the 
same time async.waterfall adds 2 levels just by default. Another thing is 

function doSomething () {
var data = {}
function a () {}
function b () {} 
a()
b()
}

or

function doSomething () {
var data = {}
a(data)
b(data)
}

function a (data) {
// body...
}

function b (data) {
// body...
}

probably it's better to create object-method for such use case

function DoSomething (data) {
this.data = data
}

DoSomething.prototype.a = function  () {
// body...
}

DoSomething.prototype.b = function  () {
// body...
} 
  

On Monday, October 15, 2012 11:18:37 AM UTC+4, Mil Werns wrote:
>
> @Tim:
> Thanks, positive feedback is always welcome :)
>
> @Eldar:
> the-box looks very interesting. I am curious to see what in this project 
> happens next. But currently, it is important for me to use only standard 
> (within the meaning of established/mainstream) componentes and libraries 
> (like e.g. async) that every node programmer knows.
>
> Can you please point out some more details why you don't find this code 
> clean/easy maintainable? And give me some example code for improvements?
>
>
> On Sunday, October 14, 2012 11:58:31 AM UTC+2, Mil Werns wrote:
>>
>> Hi,
>>
>> I want to implement a REST API with node.js and restify. Because it is my 
>> first node.js project, I spent some time to learn how to structure the 
>> application to avoid the callback hell. Please have a look to the following 
>> solution.
>>
>>
>> *Assumptions*
>> - This approach is for writing an end-user application, not a module or a 
>> driver in the sense of a public library.
>> - REST services are closely related to CRUD, so we have a lot of simple 
>> sequential/interdependent operations (get_data + validate_data [+ 
>> change_data] + save_data).
>>
>>
>> *Goals*
>> *===> Focus is writing maintainable, clean and easy to understand code! 
>> <===*
>> Conversely, this means...
>> - Performance is secondary (scaling horizontally if necessary).
>> - Avoid "low level" methods like process.nextTick(), instead use "high 
>> level" methods/libs like middleware and control flow modules.
>> - Use modules to implement a well known MVC structure.
>>
>>
>> *Code*
>> Please ignore the poor logging, the not very useful view tasks etc. in 
>> the following example. It should only demonstrate the MVC / callback 
>> structure.
>>
>>
>> */* server.js */*
>>
>> var restify = require("restify");
>> var router = require("./router");
>> ...
>> router.route(server);
>> server.listen(...);
>>
>>
>> */* router.js */*
>>
>> var userController = require("./controllers/user");
>> ... // more controllers
>>
>> exports.route = function route(server) {
>>   server.get("/users",
>>     [
>>       userController.checkRead,
>>       userController.read,
>>     ]
>>   );
>>   server.put("/users",
>>     [
>>       userController.checkUpdate,
>>       userController.update,
>>     ]
>>   );
>> ...
>> };
>>
>>
>> */* controllers/user.js */*
>>
>> var restify = require("restify");
>> var async = require("async");
>> var check = require('validator').check;
>> var sanitize = require('validator').sanitize;
>> var m = require("../models/user");
>> var v = require("../views/user");
>> ...
>>
>> var checkUpdate = function userCheckUpdate(req, res, next) {
>>   // validate user input
>>   try {
>>     check(req.params.id, "id").isInt();
>>     check(req.params.forename, "forename").len(1,50);
>>     check(req.params.surname, "surname").len(1,50);
>>     check(req.params.email, "email").isEmail();
>>   } catch(err) {
>>     if(err) {
>>       return next(new restify.InvalidArgumentError("Invalid arguments"));
>>     }
>>   }
>>   return next();
>> };
>>
>>
>> var update = function userUpdate(req, res, next) {
>>   // create model
>>   var model = {
>>     forename: sanitize(req.params.forename).xss(),
>>     surname: sanitize(req.params.surname).xss(),
>>     email: sanitize(req.params.email).xss(),
>>   };
>>
>>   //*** "sub-methods" part ***
>>
>>   // _example_ for data check
>>   function checkOwner(curData, callback) {
>>     if(curData.ownerId !== req.userId) {
>>       return callback(new 
>> restify.InvalidArgumentError("NotAuthorizedError"));
>>     }
>>     return callback(null, curData);
>>   };
>>
>>   // _example_ for simple model manipulation
>>   function addSimple(model, callback) {
>>     model.updateDate = new Date().getTime();
>>     return callback(null, model);
>>   };
>>
>>   // _example_ for complex model manipulation
>>   function addComplex(model, callback) {
>>     // use async again to simulate synchronous/parallel/... flow
>>     async.waterfall(
>>       [
>>         // do something
>>       ]
>>       function(err, model) {
>>         if(err) {
>>           return callback(new restify.InternalError());
>>         }
>>         return callback(null, model);
>>       }
>>     );
>>   };
>>
>>
>>   // *** main part ***
>>   // use async to simulate synchronous flow
>>   async.waterfall(
>>     [
>>       function(aNext) { m.get(req.params.id, aNext); },
>>       function(curData, aNext) { checkOwner(curData, aNext); },
>>       function(curData, aNext) { addSimple(model, aNext); },
>>       function(model, aNext) { addComplex(model, aNext); },
>>       function(model, aNext) { m.update(model, aNext); },
>>       function(model, aNext) { v.update(model, aNext); },
>>     ],
>>     function(err, model) {
>>       if(err) { return next(err); }
>>       res.send(200, model);
>>     }
>>   );
>>   return next();
>> };
>> ...
>>
>>
>> */* models/user.js */*
>> ...
>> var get = function userGet(id, callback) {
>>   var curData = ... // DB query for ID
>>   if(err) {
>>     console.log("DB error: " + err);
>>     return callback(new restify.InternalError());
>>   };
>>   return callback(null, curData);
>> };
>>
>> var update = ...
>> ...
>>
>>
>> */* views/user.js */*
>> ...
>> var update = function userUpdate(model, callback) {
>>   // adjust model for output
>>   delete model.updateDate;
>>   ...
>>   callback(null, model);
>> };
>>
>>
>> As you can see, this approach uses the middleware concept + async module 
>> to separate the application in small (MVC) pieces that are hopefully easy 
>> to understand.
>> But as I said before: I'm a node.js newbie :)
>>
>> *What do you think about this structure?
>> Are there any drawbacks?*
>>
>> Thanks a lot for your help
>> Mil
>>
>>
>>

-- 
Job Board: http://jobs.nodejs.org/
Posting guidelines: 
https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

Reply via email to