Mediation and data exchange between different modules

I'm just trying to raise my head on JS, because please carry me. There are different types of modules in my application. Some just encapsulate the data, others manage part of the DOM. Some modules depend on others, sometimes one module depends on the state of several other modules, but I do not want them to communicate directly or transfer one module to another for easy access. I tried to create a simple script to illustrate my problem (indeed, the modules, of course, are much more complicated):

I have a dataModule that just provides some data:

var dataModule = { data: 3 }; 

There is a configModule that provides modifiers to display this data:

 var configModule = { factor: 2 }; 

Finally, there is a displayModule that combines and displays data from two other modules:

 var displayModule = { display: function(data, factor) { console.log(data * factor); } }; 

I also have a simple pub-sub implementation, so I can simply mediate between such modules:

 pubsub.subscribe("init", function() { displayModule.display(dataModule.data, configModule.factor); }); pubsub.publish("init"); // output: 6 

However, in this way I seem to get an intermediary who must explicitly know all instances of the module - is there even a way to avoid this? Also, I do not know how this will work if there are several instances of these modules. What is the best way to avoid global instance variables? I think my question is, what would be the most flexible way to manage something like that? Am I on the right track, or is this completely wrong? Sorry for not being very accurate with my question, I just need someone to push me in the right direction.

+8
javascript events event-driven publish-subscribe
source share
2 answers

You are on the right track, I will try to give you this extra push that you are talking about:


You need a free connection, pub-sub is a good way to go.

But you really do not need this "intermediary", each module should ideally be autonomous and encapsulate its own logic.

This is done as follows: each module depends on the pubsub service, subscribes to all relevant events and acts on them. Each module also publishes events that may be related to others (code samples in a minute, carry me).

I think the bit that you might miss is that modules that use events are unlikely to become just ordinary models. They will have some logic in them and can also hold the model (which they update when events are received).

So, instead of dataModule , you will have more dataLoaderModule , which will publish the data model (for example, {data: 3} ) as soon as it finishes loading.

Another big requirement that you ask is data sharing, avoiding global instance variables - this is a very important concept, as well as a step in the right direction. What you missed in your solution for this is dependency injection, or at least a modular system that allows you to define dependencies.

You see, an event-driven application does not necessarily mean that every piece of code must communicate using events. The application configuration model or the service is what I would like to introduce (when using DI, for example, in Angular), what is required (when using AMD / CommonJS) or import (when using ES6 modules).
(that is, and then exchange data with a utility that uses events).

It is not clear in your example whether configModule static application configuration or some kind of handle that I can configure from the user interface. If this is a static config application - I would enter it.

Now consider a few examples:


Assuming the following:

  • Instead of dataModule we have dataLoaderModule
  • configModule is a static configuration model.
  • We use AMD modules (and not the ES6 modules that I prefer), because I see that you stick to using only ES5 functions (I don't see classes or constants).

We would have:

data-loader.js (also known as dataLoaderModule)

 define(['pubsub'], function (pubsub) { // ... load data using some logic... // and publish it pubsub.publish('data-loaded', {data: 3}); }); 

configuration.js (aka configModule)

 define([], function () { return {factor: 2}; }); 

display.js (aka displayModule)

 define(['configuration', 'pubsub'], function (configuration, pubsub) { var displayModule = { display: function (data, factor) { console.log(data * factor); } }; pubsub.subscribe('data-loaded', function (data) { displayModule.display(data, configuration.factor); }); }); 

What is it.

You will notice that there are no global variables (not even pubsub), instead we require (or introduce) our dependencies.


Here you may ask: β€œwhat if I meant that my configuration has changed from the user interface?”, So let's see also:

In this case, I would rather rename configModule to settingsDisplayModule (following your naming convention).

In addition, in a more realistic application, user interface modules usually contain a model, so do this too.

And it also allows you to call them "views" instead of "displayModules", and we will have:

data-loader.js (also known as dataLoaderModule)

 define(['pubsub'], function (pubsub) { // ... load data using some logic... // and publish it pubsub.publish('data-loaded', {data: 3}); }); 

settings-view.js (aka settingsDisplayModule, aka config)

 define(['pubsub'], function (pubsub) { var settingsModel = {factor: 2}; var settingsView = { display: function () { console.log(settingsModel); // and when settings (aka config) changes due to user interaction, // we publish the new settings ... pubsub.publish('setting-changed', settingsModel); } }; }); 

data-view.js (aka displayModule)

 define(['pubsub'], function (pubsub) { var model = { data: null, factor: 0 }; var view = { display: function () { if (model.data && model.factor) { console.log(model.data * model.factor); } else { // whatever you do/show when you don't have data } } }; pubsub.subscribe('data-loaded', function (data) { model.data = data; view.display(); }); pubsub.subscribe('setting-changed', function (settings) { model.factor = settings.factor; view.display(); }); }); 

What is it.

Hope this helps :)

If not, comment!

+1
source share

You do not need an intermediary. Just import the data, configure and display and call display(data, config) where you need it.

 // import data // import config function render(){ display(data, config) } 
+1
source share

All Articles