Synchronous architecture with asynchronous repository

95 views
Skip to first unread message

Adam

unread,
Oct 21, 2015, 11:01:32 AM10/21/15
to nodejs
In order to keep clean architecture of my node.js microservice I have controllers, services and e.g. repositories. 

I want a synchronous data flow: controller -> service -> repository -> service -> controller. In this flow (in simple user story) repository returns data to service and service to controller. But repository should process requests to external storage in a asynchronous way. All asynchronous code should exist only in repository, service should obtain raw data.

My current implementation:

UserController.js

    module.exports.create = function() {
        console.log("In controller: before service call");
    
        let userDto = JSON.parse(this.request.body);
        let savedUser = userService.createUser(userDto);
    
        console.log("In controller: after service call");
    };

UserService.js

    module.exports.createUser = function createUser(userDto) {
    
        let user = require('../domain/user/User');
    
        user.login = userDto.login;
        user.password = userDto.password;
    
        let userRepository = require('../infrastructure/user/UserRepository');
    
        Q.spawn(function* () {
            console.log("In service before promise resolve");
    
            let savedUser = yield userRepository.createUser(user);
    
            console.log("In service after promise resolve");
    
            return savedUser;
        });
    
        console.log("In service: after repository call");
    };

UserRepository.js

    module.exports.createUser = function createUser(user) {
        console.log("In repository: before save call");
    
        return new Q.Promise(function(resolve, reject) {
    
            userEntity.save(function(err, savedUser) {
    
                console.log("In repository: inside callback after save call");
    
                if (err) {
                    console.log("In repository: inside callback before reject");
                    reject(Error('Błąd zapisu danych!'));
                } else {
                    console.log("In repository: inside callback before resolve");
                    resolve(savedUser);
                }
            });
        });
    };

Logs:

- In controller: before service call
- In service before promise resolve
- In repository: before save call
- In service: after repository call
- In controller: after service call
- In repository: inside callback after save call
- In repository: inside callback before resolve
- In service after promise resolve

I would like to get the following sequence of logs:

- In controller: before service call
- In service before promise resolve
- In repository: before save call
- In repository: inside callback after save call
- In repository: inside callback before resolve
- In service after promise resolve
- In controller: after service call

stephane chretien

unread,
Oct 26, 2015, 4:11:06 PM10/26/15
to nodejs
Hi Adam,

there is several way to achieve this.
first you should know Q.spawn return a promise, so you can chain it as follow:
UserService.js

    module.exports.createUser = function createUser(userDto) {
    
        let user = require('../domain/user/User');
    
        user.login = userDto.login;
        user.password = userDto.password;
    
        let userRepository = require('../infrastructure/user/UserRepository');
    
       return Q.spawn(function* () {
            console.log("In service before promise resolve");
    
            let savedUser = yield userRepository.createUser(user);
    
            console.log("In service after promise resolve");
    
            return savedUser;
        }).then(function(savedUser){
           console.log("In service: after repository call");
           return savedUser;
        });        
    };

now you must also update UserController.js to use generator or promise an example with generator

    module.exports.create = function() {
        return q.spawn(function *(){ 
          console.log("In controller: before service call");
    
          let userDto = JSON.parse(this.request.body);
          let savedUser = yield userService.createUser(userDto);
    
          console.log("In controller: after service call");
       });
    };
please notice that now create will also return a promise

now more generator based solution using yield *:
UserController.js

    module.exports.create = function() {
        return Q.spawn(function* () {
            console.log("In controller: before service call");
        
            let userDto = JSON.parse(this.request.body);
            let savedUser = yield * userService.createUser(userDto);
        
            console.log("In controller: after service call");
        });
    };

UserService.js

    module.exports.createUser = function createUser(userDto) {
    
        let user = require('../domain/user/User');
    
        user.login = userDto.login;
        user.password = userDto.password;
    
        let userRepository = require('../infrastructure/user/UserRepository');
       
        console.log("In service before promise resolve");
    
        let savedUser = yield userRepository.createUser(user);
    
        console.log("In service after promise resolve");
    
        return savedUser;
       
    };

again UserController.create return a promise

Br
stephane

stephane chretien

unread,
Oct 27, 2015, 1:02:36 PM10/27/15
to nodejs
Hi Adam,

I've a mistake on the 'generator oriented' solution:



UserController.js

    module.exports.create = function() {
        return Q.spawn(function* () {
            console.log("In controller: before service call");
        
            let userDto = JSON.parse(this.request.body);
            let savedUser = yield * userService.createUser(userDto);
        
            console.log("In controller: after service call");
        });
    };

UserService.js

    module.exports.createUser = function * createUser(userDto) {
    
        let user = require('../domain/user/User');
    
        user.login = userDto.login;
        user.password = userDto.password;
    
        let userRepository = require('../infrastructure/user/UserRepository');
       
        console.log("In service before promise resolve");
    
        let savedUser = yield userRepository.createUser(user);
    
        console.log("In service after promise resolve");
    
        return savedUser;
       
    };

On Wednesday, October 21, 2015 at 5:01:32 PM UTC+2, Adam wrote:

Alexey Petrushin

unread,
Nov 8, 2015, 11:34:59 AM11/8/15
to nodejs
Use fibers, it would allow you to write plain good old Ruby / Python like code without callbacks & at the same time preserve non-blocking node.js features.

it looks like synchronous code, but it's not blocking.
Reply all
Reply to author
Forward
0 new messages