You’ve got some HTML and it has a button that says “edit”. That button gets clicked. There’s a click event handler behind the button, but behind the event handler, there’s more code to run and work with. There’s an entire stack of front-end JavaScript in your Ember, Angular, Backbone, React or whatever code it is.
But, at what point does that “edit clicked” event (past tense language, like an event should be) turn in to an “edit this thing” command? And does even turn in to a command, necessarily?
Events vs Commands
The use of language and the expectations language creates is important. Without this understanding, code gets confusing, quickly because we don’t know what to expect or what the intention of the code is.
Before the code is written, though, there is an underlying understanding of what is happening that needs to be examined. It’s in this space that the need for a separation between events (things that have happened) and commands (an order to do work) exists.
Events
Events are past-tense. They are something that happened already. A button was clicked. A dog jumped. A horn honked. These event descriptions tell us what happened, and sometimes the subjects involved in that thing happening.
We tend to model event-driven code around the present tense of an event, with functions like `onClick` or `on(“click”)` – but the reality of this “click” event is that it has already happened. It is past tense. Our event handler code does not fire before the button has been clicked, or during the click – these are other events that can be handled. No, the button click event fires after the click occurs.
When an event is triggered, zero or more listeners may respond to the event. The event publisher doesn’t care if there are any subscribers, or not. It publishes the events and if there are any subscribers around, fine. If there none, fine.
Commands
Commands are future-tense. They are an order to do something in the future. Dog, jump. Cat, run. Car, stop! These commands tell the subject what to do.
Commands are often modeled as method calls or other API calls – they can be as simple as `dog.jump()`, as abstract as `commandSystem.execute(dog, “jump”)` and beyond. But the reality of a command is that it is a future-tense order to do something. Commands are ordered prior to the work being done. You don’t call `dog.jump()` while the dog is already jumping. You make that API call and then the dog jumps.
When a command is ordered, there needs to be something around to handle it or work won’t get done. There may be a queue of available workers with some means of picking which one(s) do the work. There may be only 1 command handler around that does everything. If there are no command handlers, though, bad things tend to happen – null errors, or work not getting done, etc.
Past vs Future
Having a basic understanding of events vs commands will shape the way you look at code. Dealing with button clicks and the code that executes as a result, is a prime example.
From Button Click Event To Command Execution
It’s easy to spot an event handler in JavaScript – typically marked by the use of an “on” method with an event name as the first parameter:
The separation between the event and the command in this example is fairly clear. The code is waiting for the button’s “click” event to fire. Once that happens, it calls some code on another object and does some work. The click event is handled, and then the doSomeWork command is called.
But it’s not always clear where the event ends and the command begins.
Blurry Gray Lines
In Backbone / Marionette applications, for example, there is often a difficult and obscure line between where the event ends and the command begins. This happens because the code of a View object is most often the HTML event handler.
But as in any good structure for a JavaScript application, you don’t want the view to be the place where logic and process happens. That should be pushed up and in to a workflow object of some sort.
So, what do you do here? Do you trigger another event from the view to mimic the event that came from the DOM? Do you trigger a new event that has more meaning to the application, than the DOM event? Do you immediately switch to command mode and call some code?
It’s not an easy choice to make, all the time.
Mimic The DOM Event
It may be easy / convenient to just mimic the DOM event. Someone clicked an “edit” button? Ok, let’s just forward the “click” event:
Click? Really? Click what? For what purpose? What was the user trying to do? This is almost universally a bad idea.
The DOM represents a user interface full of widgets and controls and things that the user interacts with. But it does not represent the purpose of those things, only the display and interaction patterns. Forwarding a “click” event from an HTML Button through the view object may seem simple, but it does not provide any information about the intention of the user interaction.
Further, when the view grows and changes and includes more than just this one button, what does “click” mean? Now you have to start naming things like “click.edit”… which sort of gets a little closer to something reasonable. Yet, it still contains a representations of the mechanical process by which the event was triggered. What happens when the button changes to a hover based event, or input from something other than a pointer?
No – mimicking the DOM event through your view is a bad idea. You should at least provide something that more closely reveals the intention of the click.
Trigger A New Event
As in every other aspect of software development, context matters.
When an HTML button element triggers a “click” event, you have the context of that element being clicked… it’s HTML, and you’re handling it. It makes sense that this is a “click” event and your code is dealing with a “click”. But, when you step outside of the context of HTML elements and events, though, “click” doesn’t make much sense anymore.
Instead, it would make more sense to trigger a new event that carries context and intention, from your view. For example, when the “edit” button is clicked, you may trigger an “edit” event from the view:
This event would be handled by the higher level workflow. At that higher level, the “edit” event would have better clarity for the intention of the event.
In spite of increased clarity, however, there’s still a problem. Using “edit” as an event implies “this thing was already edited”. But in this situation, the intention is to say “this thing needs to be edited”.
Does that mean we need a command to execute from within the view, at the point where the DOM event is handled?
Execute A Command
There are two basic ways to execute a command from within the view in question.
- Call the command, directly
- Use a generalized command API
If the view wishes to execute the command directly, it needs to have a reference to the object on which work is being ordered. Pass the object in to the view with other options, and then call that object directly from within the view.
If you’re skin is crawling right now, that’s a good thing. This is the kind of tightly coupled, difficult to maintain code that was fixed by adding a high level workflow object to your application.
Rather than passing in the specific object on which the command will be executed, it would be better to provide a more generalized command API. This could be done in many forms – the simplest of which is just a function.
This doesn’t look like much of a difference – because it isn’t. The main advantage here, is that you have at least a little separation between the command API and it’s implementation. The high level workflow object can construct the specific command it needs, and the view only hast to call it.
If the command implementation needs to change – call a different object, a new set of behaviors, do something entirely new – it can. The command is now separated from the implementation, with the implementation controlled by the higher level workflow. This also opens opportunity for a more generalized command API (another discussion for another time).
Overall, the command execution idea provides a better separation of concerns and cleaner code. But there are still some large gray areas to deal with, leaving room for interpretation in specific situations.
No Silver Bullets
The idea of a command makes sense from a lot of perspectives. Yet it introduces a different level of coupling in the code. Now the high level workflow object needs to know which “command” API to fulfill for the view to operate correctly.
This could be solved with a more abstract form of a command pattern where the view executed named commands. That leads to the view being tightly coupled to the knowledge of which command to execute, though. This is the definition of semantic coupling, and is easily the most dangerous form of coupling around.
With the potential downsides of commands being executed from the views, I’m personally left with a big question mark of what makes the most sense. It’s clear to me that an event from a view becomes a command as soon as the event handler invokes any code. What’s not clear, however, is which of the patterns I’ve shown should be used, and in what circumstances.
More often than not, I end up using the event approach with events named for the appropriate context. This keeps the code a little less coupled even if I’m breaking the semantics of events vs commands for a moment. But there have been times where I went with the command route, passing a command object or method down in to a lower level to be executed as needed.
It seems to me, that there needs to be more than just events bakes in to Backbone/Marionette views. Perhaps an inclusion of commands that can be executed from within the view and handled as needed from the workflow object.
Pick Something And Run With It
In the end, I don’t think there is a right or wrong answer for how you handle the specifics of moving from an event to a command. It may be done explicitly with a command object / method. It may be done implicitly with objects and methods being called as needed.
However you decide to handle commands from views, standardize that method and stick with it until you see that it no longer works for your app. The only thing worse than picking a bad implementation pattern, is not picking one and ending up with an inconsistent mess across your application.