you can use domains to replace the err handling with centralized approach

Am Mittwoch, 17. Oktober 2012 09:48:56 UTC+2 schrieb Mil Werns:
>
> @John: I'm using couchDb. Currently, my db-query function is just a 
> wrapper around the "request" module. Perhaps I will use the nano module in 
> the future.
>
> @Manuel: For small projects, I prefer a central routing module, but for 
> bigger projects you are right of course. Thanks for this hint and the mvc 
> link.
>
> @Eldar, @greelgorke: I think you are right that the async module shouldn't 
> be used for simple functions. I will adjust my project code. The only thing 
> that bothers me is the costly error handling
>
> m.get(..., function(err, body) {
>   if(err) { return next(err); }
>     m.upate(..., function(err, model) {
>       if(err) { return next(err); }
> ...
>
> A lot of same "*if(err)*" lines. So, using the async module for 
> _complicated_ (deep nested) cases keeps the code cleaner.
>
> @All: Thanks for your responses, I will optimize my code :)
>
>
>
>
> On Tuesday, October 16, 2012 1:58:28 PM UTC+2, greelgorke wrote:
>>
>> totally agree about nesting and async is a smell.
>>
>> Am Dienstag, 16. Oktober 2012 10:58:22 UTC+2 schrieb Eldar:
>>>
>>> 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