Login Register

Architecture

Laura, being the agile programmer she is, wants to first build the smallest solution that solves the problem. She sighs, longing for the days of C and network sockets. But JavaScript's security model prohibits peer-to-peer networking, so there's nothing like that.

She looks on the Dojo site and finds the cometd (pronounced comet-d) server. Interesting! It's a lightweight, HTTP-based server with a small footprint. And it is supposed to have good Dojo integration through publish-subscribe events.

Publish-Subscribe

Publish-subscribe is an easy-to-understand model for cross-process communication, and Dojo has it baked in. It effectively promotes loose coupling between components. Suppose you have a tax form similar to Example 1. You have ten dijit.form.CurrencyTextBox's whose contents get added up to a Gross Income, another dijit.form.CurrencyTextBox. How do you keep Gross Income updated? One way is for the component CurrencyTextBox's to call a central procedure for updating:

<script type="text/javascript">
function reAdd() {
   newSubtotal = dijit.byId("wages").getValue() + ...;
   dojo.byId("grossIncome") = newSubtotal;
}
</script>
<div dojoType="dijit.form.CurrencyTextBox" id="wages">
   <script type="dojo/connect" event="onChange">
      reAdd();
   </script>
</div>
<div dojoType="dijit.form.CurrencyTextBox" id="tips">
   <script type="dojo/connect" event="onChange">
      reAdd();
   </script>
</div>
...

Because this system is tightly coupled, problems arise:

  • If you add an 11th line to total up, you must change the reAdd() procedure.
  • If half of the boxes need to send extra onChange events to computeDeductions(), those half need to be changed to call this method.

Granted, this example is contrived because the methods are so small. But the larger your system, the more nightmarish maintenance becomes. It would be nice to have a whiteboard where controls could sign up for events that interest them. This whiteboard could be maintained by a third party to keep the controls from worrying about communication details.

That's the essence of publish-subscribe. Dojo is the whiteboard maintainer. The GrossIncome box then says "I'm interested whenever a box changes." This is called a topic, and we say the GrossIncome box subscribes to that topic. The component boxes agree to publish information on this topic when they change. Here's how it looks like in code:

<script type="text/javascript">
function reAdd(args) {
   dojo.byId("grossIncome").setValue(
        dojo.byId("grossIncome").getValue()
        + args.newValue
        - args.oldValue
   );
}
</script>
<div dojoType="dijit.form.CurrencyTextBox" id="GrossIncome">
   <script type="dojo/method">
      dojo.subscribe("componentChange", reAdd);
   </script>
</div>
<div dojoType="dijit.form.CurrencyTextBox" id="wages">
   <script type="dojo/connect" event="onChange" args="e">
      dojo.publish("componentChange", { oldValue: e.oldValue, newValue: e.targetNode.value });
   </script>
</div>
...

So the topic "componentChange" is published when any of the component boxes change. Sent along with the topic are message-specific data elements that help the subscriber - oldValue and newValue in this case. Need to find which elements in the event object they are.. The lone subscriber, the GrossIncome box, uses these to adjust the current sum. Now to add a component box, you just add it to the form with the same dojo.publish statement. Very nice.

Publish-Subscribe With Cometd

Cometd effectively extends Dojo publish-subscribe past the browser. Cometd speaks the Bayeaux protocol which itself runs over HTTP. Laura immediately sees the usefulness of this for online support. The client can publish a topic to Cometd containing the message. The operator subscribes to that topic and displays messages on the console. It replies on that same topic. Both operator and client send the userid with the message, so they can ignore the message they themselves send.

The protocol works like this. Assume the client's username is "alex.russell" and operator's username is "operator".

  • Operator:
  • (Time Marches On...)
  • Client: Subscribe to "/chat/demo/alex.russell".
  • Client: Publish a message on "/chat/demo/alex.russell": { user: "alex.russell", join: true, chat : "alex.russell has joined the room."}
  • Client: Publish a message on "/chat/demo": { user: "alex.russell", joined: "alex.russell"}
  • Operator: (Sees message on /chat/demo) Subscribe to "/chat/demo/alex.russell"
  • Client: Publish a message on "/chat/demo/alex.russell": { user: "alex.russell", chat : "I have a question about Dojo."}
  • Operator: (Sees message on /chat/demo/alex.russell) Publish a message on "/chat/demo/alex.russell": { user: "operator", chat : "Shoot."}
  • Client: (Sees message on /chat/demo/alex.russell) Publish a message on "/chat/demo/alex.russell": { user: "alex.russell", chat : "Oh wait, I invented Dojo. Never mind."}
  • Client: Unsubscribe to "/chat/demo/alex.russell"
  • Client: Publish a message on "/chat/demo/alex.russell": { user: "alex.russell", leave: true, chat : "alex.russell has left the room."}

Although the protocol looks a little "chatty", Laura has designed it with public chat rooms in mind. When those come to pass, users will be able to join arbitrary public chat rooms using this protocol.

This upfront design will payoff - coding will go quite rapidly.

Details for the Impatient