Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dojo: topics vs events, what design considerations should be taken in account?

I've been using Dojo in various contexts and never found a good explanation on events versus topics. What I understand from using both mechanisms is the following:

  • Both are event or more generally message mechanisms.
  • Both work more or less the same, in that you subscribe to a topic/event by setting a callback.
  • Events are tightly coupled to an object/widget, as in, you need the actual instance of an object or widget to register listeners for specific events.
  • The topic mechanism on the other hand provides a more decoupled approach, as you can subscribe for any topic without knowing which component is publishing the topic, or even without knowing if the topic will be published at all.

An approach I used a couple of times when developing custom widgets with Dojo was by letting them publish to certain topics. Other components would subscribe to these topics and react appropriately. However, this leads to code that is hard to follow, because when you find a piece of code that subscribes to a certain topic, you start wondering who is publishing to that topic and vice versa. Currently I tend to let my custom widgets submit events and have a controller listening to these events and dispatch them to other widgets that should react on these events.

So in the first approach, the topic mechanism is the glue between widgets, but it is decentralized which makes it hard to maintain the code on the longer term in my experience. In the second approach, a controller class (following the MVC pattern) is the glue, which centralizes event handling.

I'd be interested in knowing if this is a correct understanding of the two mechanisms. I'd also be interested in any design consideration one should take in account when choosing one of the two (or mix them even?). Any pointers to an elaborate discussion on the topic would be appreciated as well. I have been looking at: http://dojotoolkit.org/documentation/tutorials/1.9/events/ but that mainly describes how both mechanisms work but give little insight in how to structure a complex application.

like image 416
Bertjan Broeksema Avatar asked Jan 30 '14 10:01

Bertjan Broeksema


1 Answers

I'm having the exact same idea about topics and events as you. As JavaScript is event-driven both are of course event-ish (like you describe in your first point).

Events are indeed coupled to the widget itself while topics aren't. I usually see it as the following:

  • When you have master-slave kind of structure (like a list having many items), then using widgets and events is probably the best approach to handle your problem.
  • When both widgets are unrelated to each other, then topics are probably the best way to communicate between each other.

You're right, topics make it harder to know what the origin is, but if you think about it, you don't need to know the origin. The topics provide you an API that decouples the source from the destination, making it so that you don't need to know the source.

Because both widgets are unrelated (that's the approach I follow, described before), you should normally don't need to know what the origin is when maintaining the code.

What you need is a well written API and make sure both source as destination are following it. If the API changes (code maintaining) you can use your IDE to find out which widgets are publishing/subscribing (for example by searching to the topic name) and make sure each of them is updated.

You can also choose to encapsulate the publish/subscribe behavior and providing a more high level API by creating a module like this:

define([ "dojo/topic", "dojo/_base/array" ], function(topic, arrayUtils) {
    var MY_TOPIC = "/my/topic";

    var module = {
        observers: [],
        notify: function(/** String */ name, /** Integer */ age) {
            topic.publish(MY_TOPIC, {
                name: name,
                age: age
            });
        },
        addObserver: function(/** Function */ callback) {
            return this.observers.push(callback) - 1;
        },
        removeObserver: function(/** Integer */ index) {
            this.observers[index] = null;
        }
    };

    topic.subscribe(MYTOPIC, function(data) {
        arrayUtils.forEach(module.observers, function(observer) {
            if(observer !== null && data.name !== undefined && data.age !== undefined) {
                observer(name, age);
            }
        });
    });

    return module;
});

You publish using the notify() function (providing the correct function parameters) and you add/remove observers with the other functions. Then you will make this component your sole subscriber and make it notify all observers.

This way you don't need to know about the topic and the API is uniform. You only need to make sure that the callbacks use the arguments correctly. To maintain your code you just change the high level API and look for modules that use this high level component. This is way easier to detect since it's in the require() function.

When I use topics I usually create a high level API like this (might change a bit depending on the use of it). But I think the point made is clear, it's easier to change the topic and to modify the data that is sent through.

like image 57
g00glen00b Avatar answered Oct 19 '22 15:10

g00glen00b