Search code examples
node.jspostgresqlexceptionnode.js-domains

How to add in node.js domain global emitter?


In my node.js application i used to handle error by domain. The architecture of the app looks like:

  1. Controllers. Express routing calls controllers methods

  2. Controllers call services and use models

  3. Services call repositories

(Actually it's quite similar to DDD).

So, controllers create domain, and run its actual body in the domain. Services used to throw exceptions if something's going wrong. Also controllers listen for domain errors and process them. Its very comfortable because i dont need to worry about carring an error over all services's method callstack — i just throw an exception and can be sure that it would be caught in controller.

But i have faced a problem connected with using PostgreSQL. I use node-postgres module and i create pg.Client in separated js file, so pg.Client is like shared for everybody (otherwise, creating pg.Client on each query makes open lots of active connections with postgres).

The problem is that when pg.Client is definded in separated file it's like a global object and it's not included in domain scope created in controllers. So exceptions throwed from pg.Client callbacks are not caught by domain.

I will show simplified way of request processing to make it clear. Let's say user wants to get login by userId:

  1. Somewhere in the beggining pg.Client created

  2. Get request comes to express, express call routing method

  3. Routing calls some of controller's method

  4. Controller creates domain and calls in «domain.run» a service

  5. Service calls a repository

  6. Repository takes pg.Client created ealier, and calls sql query method

  7. Result of sql query method puts in callback (so this callback is called by pg.Client)

  8. And then the callback is processed in service, where we check that we get null instead of user model (because there is no such user in db for given userId) and throw an exception

So that exception is not caught in domain. Technically, we need to use «add» method of node domain and add pg.Client, but how i said pg.Client is shared, hence it would be added in different concurrent domains.

I will list some code example to make it more clear. It's simplified method of UserService:

 login: function (email) {

                    var userRepository = new UserRepository();
                    userRepository.findByEmail(email, function (model) {
                        if (model == null)
                           throw new Error('No such user');
                    });

            }

So, that method «login» is called in domain. But it created userRepository, calls findByEmail method which will use shared pg.Client laying outside of domain's scope, and that's why exception will not be caught in domain.

Any ideas how to fix it and put pg.Client in the domain?


Solution

  • I solved the issue. I create EventEmitter in scope of active domain and listen lets say «onCallback» event. In callback of query method (which is not connected with the domain, because pg.Client is kinda global and lays out of the domain) i emit «onCallback» of the EventEmitter.