A long time ago, in what seems to be a previous life at this point, I wrote a small blog post about modeling and creating an explicit return value from a dialog form in a Windows application. Fast forward a lifetime and I’m finding that this knowledge and experience is resurfacing itself in my daily work with. Whether it’s Backbone and Marionette or Node.js and RabbitMQ, I’ve used this pattern that I first learned in WinForms and my applications have benefited greatly from it.
A Poorly Constructed Workflow
Consider this example: a human resources application allows you to add a new employee and select a manager for the employee. After entering a name and email address, the form to select the manager should be shown. When save is clicked, the employee should be created. A crude, but all too common implementation of this workflow might look something like this:
Can you quickly and easily describe the workflow in this example? If you can, you probably paid attention to the description above. Look at the code again, and follow the workflow through the code.
Personally, I have to spend a fair amount of time looking at the implementation details of both views in order to see what’s going on. I have to piece together the bits of the workflow from multiple places to form a more coherent, high level overview in my head. It’s not easy for me to see what’s going on. Every time I look at one part of the code, I have to mentally dig through implementation details that cloud the vision of the high level workflow. This is time consuming and prone to mistakes.
Too Many Concerns
This code has a number of different concerns mixed in to very few objects, and those concerns are split apart in some rather un-natural ways. To understand the complete concern, code from different parts of the app have to be mentally put back together. But, what are the concerns that are presented in this code?
The first set of concerns are found in the high level workflow:
- Enter employee info
- Select manager
- Create employee
The second set of concerns are what should be the implementation detail:
- Show the EmployeeInfoForm
- Allow the user to enter a name and email address
- When “next” is clicked, gather the name and email address of the employee
- Then show the SelectManagerForm with a list of possible managers to select from
- When “save” is clicked, grab the selected manager
- Then take all of the employee information and create a new employee record on the server
This list doesn’t even cover all of the edge cases or common scenarios. What happens when the user hits cancel on the first screen? Or on the second? What about invalid email address validation? Adding these steps to the list of implementation details has things getting out of hand very quickly.
By implementing both the high level workflow and the implementation detail in the views – the details and implementation – the ability to see the high level workflow at a glance has been destroyed. This will cause problems as details will be forgotten when changing the system. Code will be broken. Features will be missing. Adding more to the process – like the validation or cancel buttons that are already missing – will make it more complicated, still.
This situation has to change.
Modeling An Explicit Workflow In Code
Instead of tightly coupling the workflow to the implementations, the high level workflow should be extracted. The governing process for this part of the application should be made explicit in the code, in a way that makes it easy to see the over-all flow.
Think of a workflow diagram as an example. The diagram doesn’t show all of the details. It shows the high level steps. Each step may be composed of additional detail, but the diagram shows it simplified in to single boxes.
Code should be modeled in the sam manner. The workflow should be high level, showing the basic steps. Detail of each step should be modeled in to other objects that are called by the workflow. This makes it easier to change the workflow and to change any specific implementation detail without having to rework the higher level process.
Consider this code, for example:
Here, the high level workflow is easier to see. After the employee info is entered, the manager selection comes up next. When that completes, the employee info is saved. It all looks very clean and simple. More importantly, the additional features like validation and cancel buttons can be added to the code. The validation may be a detail that happens in the individual form, but the cancel button is likely to be a part of the high level workflow.
From here, at the workflow level, moving to the details can be accomplished with a couple of Backbone views and a model for the details:
(I’ve omitted some of the details of the views and model, but I think the idea is there)
There are a number of benefits to writing code in this manner:
- It’s easy to see the high level workflow
- You don’t have to worry about all of the implementation details for each of the views or the model when dealing with the workflow
- You can change any of the individual views without affecting the rest of the workflow
- Adding new features or process to the workflow is easier
- (and more!)
Of all the benefits listed and the ones that I am not thinking of at the moment, the most important one may be the ability to see the high level workflow. 6 months from now – or if you’re like me, 6 hours from now – you won’t remember that you have to trace through 5 different Views and three different custom objects and models, to piece together the workflow. But if you have a workflow modeled explicitly, you’re more likely to pick up the code and understand the process quickly.
Everything has a price, right?
The real cost, though, is learning new implementation patterns to get this working. That takes time. Sure, looking at an example like this is easy. But it’s a simple example and a simple implementation. When you get down to actually trying to write this style of code for yourself, it will be more complicated. There’s no simple answer for this problem, either. You have to learn through it in order to improve your application design.
It’s Worth It
In the end and in spite of potential drawbacks and learning curves, explicitly modeling workflow in your application is important.
As with any good architecture and philosophy, the principles are the same across languages and other boundaries. It’s only the implementation details that change.
(This article was originally published on LosTechies, and has been revised and edited here)