While working at REDACTED, I reported the following issue.
Our apps are based on Flow classes that define which pages they manage and how those pages are interconnected according to pressed buttons and input data.
Unfortunately,
- it’s needlessly complicated, basically due to a misunderstanding of how a highly configurable system is usually implemented nowadays;
- it’s a layer on top of Angular, but it’s not Angular, which means that an expert Angular programmer must learn it before being productive;
Additional drawbacks of our flow implementation:
- They are meant to reuse pages but then, for example, a page which is at the same time used for creating and editing a resource is plagued by continuous branching to account for all the little differences between creating a resource and editing it afterwards. (what about components inheritance?)
- They are meant to reuse pages but not flows themselves, so, given that a flow is associated to a set of URLs which change from creating to editing (continuing with the example above), those two practically identical flows for creating and editng must be represented by two separate classes.
These flows force us to
- show page 1 to the user and wait for her to press a navigation button,
- after she presses a navigation button, move captured data from page 1 to its flow,
- make the flow decide that page 2 is the page following page 1, according to the current state,
- move those captured data (and possibly some more) from the flow to page 2,
- show page 2 to the user and wait for her to press a navigation button,
- …
which means that these flows force us to write lots of code which is hard to follow, because it’s spread in two different classes and the logic used to determine the next page strongly depends on the state of the current page but the configuration of that state is much more richly documented by the page class (that defines it) than the flow class (that uses it).
Additionally, many flows of ours are fake flows because they only are a means to capture data of complex entities with a very big form split into many pages, connected to one another in a linear fashion. The user can only move back and forth, from start to end. And the only exceptions to linearity are conditional skips.
We could just use a store to preserve state between pages and then
- tell pages that they belong to the sequence: page 1, page 2, …
- show page 1 to the user and wait for her to press the back or next button,
- show page 2 to the user and wait for her to press the back or next button,
- …
While fake flows are justly tailored and not reusable, there are also a few flows like the confirmation flows that are real and should be addressed with reusable flows. In our app, confirmation flows work like this:
- On an ORIGIN page, the user selects some documents to act upon, e.g. to delete them.
- After the user presses the
Delete
button, the confirmation flow starts.- The confirmation page opens and lists the selected documents.
- The user can unselect some of them. (to not delete them anymore)
- The user can open and read some of them. (to make sure she really wants to delete them)
- The user can cancel the operation altogether, and get back to the ORIGIN page.
- The user can accept the operation on the remaining documents.
- If they could all be deleted, the ORIGIN page will show a success message.
- If they couldn’t all be deleted, the ORIGIN page will show a failure message.
- When the ORIGIN page is loaded again, all documents that the user had selected are still selected (if they appear in the list).
That is a small flow which could benefit from a reusable architecture. Instead of deleting, it could be about accepting or rejecting documents, copying them, archiving them, … whatever.
It could work like this:
originPage.deleteSelectedDocuments = () => {
ConfirmationFlow.show({
selected: originPage.getSelectedArticles(),
onAccept: [Article, 'moveToTrash'],
});
}
but our current implementation of flows doesn’t allow anything like that.