Briefly about the structure of what is. When starting the application

server.listen(port); server.on('error', onError); server.on('listening', onListening); require('socket')(server); 

The line require('socket')(server) - connects the file:

 module.exports = function(server){ var io = require('socket.io')(server); io.set('origins','*:*'); //io.set('origins','localhost:*'); io.on('connection', function (socket) { log.info("Socket is connect"); }); }; 

Now, more to the point, in the application there are controllers that are inherited from the base:

 var BaseController = require("core/base/baseController"); class AccountPageController extends BaseController{ constructor(params){ super(params); //more actions ... } methodForPage (){ //more actions ... } } module.exports = AccountPageController; 

And, let's say, there is a need to send a message like this in methodForPage() :

 socket.emit('msg',{text:'server say:' + new Date()}); 

But, the variable socket is available only inside io.on('connection') . How to make it available inside the controller? Or how to define it inside the base controller so that it is available in all controllers?

And there is one more thing, for example, there is a need to set up a socket event inside the controller, for example:

 methodForPage (){ //more actions ... socket.on('msg', (data) => { //do record data to db this.dbh.query(/*more actions*/); }); //more actions ... } 

At the same time, we want to use the properties of this controller to, for example, write data received from a socket to the database.

Is it possible to implement all of the above in this form, and if so, what should be done for this? Is this the right approach?

  • Your problem is not in socket.io, but in architecture. The simplest thing you can advise is to replace var io = with global.io = and it will be visible from everywhere. But note that this is not best practice at all - Darth
  • thanks, and best practice what then is? Give any examples that they will specifically decide my task, I will consider / study a lot of time - sanu0074
  • one
    There are lots of options. I prefer architecture in the form of a tree, where there is something like a god object, from which all modules are available, which acts as a dispatcher between them and a single access point for all parts of the application. Usually, I either have it in global, or it is transmitted to all constructors / modules. But not the fact that this is the best solution, for each case you need to look separately. Hopefully, they will write you a full answer on this topic. - Darth
  • 2
    Well, it's about goto. It is much easier to shoot yourself in the foot (in this case, to grind something important from the global area), besides, if you put everything in the global, there is a chance that the garbage collector will collect less. - Darth
  • 2
    God Object is also an anti-pattern. The correct decision is IoC, but it is, for some reason, not popular with javascript programmers. The main problem of global - isolation of dependencies (testing, etc.). - Dmitriy Simushev

1 answer 1

You are faced with a typical OOP approach: where do you get dependencies from?

There are several solutions to the problem. The most obvious is the use of global variables , but this option is only suitable for very small projects or projects that do not need to be supported. A very large number of different articles have already been written about why global variables are harmful. All problems come from one very simple fact: the code becomes too tight (tight coupling). As a consequence, this results in the impossibility of code reuse, problems with unit testing, difficulty in debugging applications, and so on. In medium and large projects, all this leads to such porridge after a while that it becomes easier to rewrite everything from scratch.

A classic solution to the problem that JavaScript developers rarely use is the Inversion of Control approach. One of the options for implementing this approach is the principle of dependency injection (Dependency Injection), in which all dependencies "descend from above". socket.io way, you already use it when you pass the server object to the module responsible for initializing socket.io .

Following this approach, you should do the following:

  1. You return an instance of socket.io from a function exported in the file socket.js :

     module.exports = function(server) { let io = require('socket.io')(server); // ... return io; } 
  2. Declare an instance of socket.io an explicit dependency of the base controller (or some route aggregator):

     class BaseController { constructor(params) { this.io = params.io; } } module.exports = BaseController; 
  3. Use an instance of socket.io in child controllers (or real routes):

     let BaseController = require('base_controller'); class FooBarController extends BaseController { constructor(params) { super(params); this.io.on('msg', this.handleMessage.bind(this)); } handleMessage() { // .... } } module.exports = FooBarController; 
  4. In the main project file, you transfer dependencies explicitly (you can use a DI container, but this seems to me somewhat redundant):

     let initSocket = require('socket'), FooBarController = require('foo_bar_controller'); // Вот здесь вы инициализируете сервер, а я для простоты использую пустой объект let server = {}; // Передаете сервер для инициализации socket.io let io = initSocket(server); // Передаете экземпляр socket.io для инициализации контролера let controller = new FooBarController({io}); // Запускаете собранное приложение server.listen(); 
  • Thank you for a very useful and extensive answer! I have not tried your advice in practice yet, but looking at the code, I have a question, it concerns clause 4. Here you write that in the project launch file, we connect a child controller and pass an instance of socket.io to it for initialization , but the fact is that in my project controllers are always initialized in routs: router.get ("/ fooBar", function (req, res) {var controller = new FooBarController (res, req); controller.viewPage ();} ); - sanu0074 4:04 pm
  • And I don’t quite understand how it will work, and why it is necessary to initialize the child controllers in the main project file (there may be a lot of them), and how will req and res get into them later? Maybe I am doing everything wrong, and the project structure is incorrectly built, I would very much like you to send me on the right path! - sanu0074 4:04 pm
  • one
    @ sanu0074, if it talks about multiple controllers, then you can easily enter an additional intermediate level, which will receive io during initialization and already initialize the controllers. Actually, when you initialize the router, the idea is the same: you export a function from the file that receives io and returns the router. Just as you pass a function to the server , which returns io - Dmitriy Simushev
  • one
    @ sanu0074, io can be transferred to the place where the router is generated, and then delivered to the controller through the constructor. as for req and res , then there may be options. I would rather use the controller methods as request handlers - Dmitriy Simushev
  • one
    @ sanu0074, To be honest, I doubt that a socket instance can normally be serialized / deserialized for storage in an arbitrary session repository. As a result, I would rather store the socket ID of the socket connection in session and, if necessary, pull out a socket instance by ID. - Dmitriy Simushev