("Kessler Syndrome <https://en.wikipedia.org/wiki/Kessler_syndrome>" is
that thing where space satellites cascade into each other, making orbit a
giant mess.)

*tldr:* We often dispatch events to trigger *required* interactions between
different modules. This is fragile. We should not use events for these
cases; we should use Service.request() or similar.

Often, a module needs to perform an action in response to triggers from
other modules. In an effort to reduce coupling, the system app relies
heavily on events for this purpose. For instance, if we need
InputWindowManager to do something in response to SomeWidget's opening, we
often do something like this:

// in SomeWidget.js:
this.publish('somewidget-opening')

// in InputWindowManager.js:
InputWindowManager.prototype.handleEvent = function(e) {
  switch(e.type) {
      case 'actionmenuhide':
      case 'activityclosing':
      case 'attentionrequestopen':
      case 'attentionrecovering':
      *case 'somewidget-opening': // this line added*
        // Do things that release the current window focus or whatever
  }
}

Here, InputWindowManager must respond the same way to a bunch of events
from different sources. While decoupling the modules with an event is a
good idea, it obscures an important module dependency. In this case, there
are a few problems:

   1. From SomeWidget's perspective, it's not clear why we emit the event.
   (Here, the sole reason we emit the event is to ensure InputWindowManager
   handles the hook at the right time.)

   2. From InputWindowManager's perspective, we now have a group of logic
   that needs to run in response to a bunch of different events. Rather than
   reducing coupling, we've just moved the explicit dependency to
   InputWindowManager, disguised as an event.

   3. This pattern contributes to "event spaghetti", wherein code becomes
   difficult to refactor and rationalize about due to the hidden entanglement
   and cascading nature of event dispatching.


For cases like this, where we _need_ a specific action to be performed, I
think we should be more explicit about what, precisely, we want to happen:

// SomeWidget.js:
Service.request('InputWindowManager.releaseWindowFocus')

Or, alternatively, dispatch an event that semantically represents our need,
no matter which module it originates from:

// SomeWidget.js:
this.publish('release-window-focus')
// ActivityWindow.js:
this.publish('release-window-focus')

There are times when it makes sense for a module to consider itself alone
and decoupled, blissfully ignorant about whether or not other modules
respond to its emitted events. However, we often *do* expect a module to
interact in a specific, well-defined way with core system facilities, like
input and window management. In these cases, there is a *necessary*
coupling between modules. Using module-specific events obscures the
coupling and leaves the system brittle.
_______________________________________________
dev-fxos mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-fxos

Reply via email to