Concepts / Building / Task-Based UIs
Building

Task-Based UIs

This guide explains task-based user interfaces: interfaces that let users express what they want to do, instead of editing records. It covers why they fit event-sourced systems, how they differ from forms over data, and how to design them.

Many applications present their data as forms. A user opens a record, changes some fields, and clicks Save. The application then receives the new values and has to guess what happened. Did the reader move, or was the address wrong all along? Was the book returned, or was its status corrected by a librarian? The form only knows that some fields are different now.

A task-based UI asks a different question: not "what should the data look like?" but "what do you want to do?" Each task is an intention the user expresses – borrow a book, extend a loan, report a book as lost – and each one becomes a command with a clear meaning.

Forms Over Data#

The classic form works on the level of storage. It shows the current state and sends back a changed state. This has consequences that are easy to overlook:

  • The intention is lost. The application sees that a field changed, but not why. An event-sourced system, which wants to record what happened, has nothing meaningful to record.
  • Rules have no place to live. Some changes are allowed only in certain situations, or only together with others. A form that lets every field be edited freely has to validate every combination after the fact.
  • Users do the translation. A librarian who wants to extend a loan has to know which fields to change, and how. The interface speaks the language of the data, not of the work.

Tasks Instead of Fields#

A task-based UI is built around the tasks users actually perform. For a loan in a library, instead of one editable form, there are separate, explicit actions:

  • Extend the loan – asking only for the new due date.
  • Return the book – asking for nothing at all.
  • Report the book as lost – perhaps asking for a note.

Each action sends one command – extend-loan, return-book, report-book-lost – and each command, if accepted, leads to events that say exactly what happened: loan-extended, book-returned, book-reported-lost. The history of the system now reads like the history of the work, because the interface captured the work, not just its results.

Why It Fits Event Sourcing#

Event Sourcing and task-based UIs belong together:

  • Commands need intentions. A decider decides on a command in the light of the current state. A command such as update-loan gives it nothing to decide on; extend-loan does.
  • Events need meaning. An event store is only as valuable as the events in it. Events named after tasks – see Modeling Events – can be understood and analyzed years later; events named loan-updated cannot.
  • Rules have a place. Each task maps to one command, and each command to one set of rules. Whether a loan may be extended is decided where extending is handled, not somewhere in a validation of changed fields.

Designing Task-Based UIs#

A few principles help to find the right tasks:

  • Start from the work. Ask the people who use the system what they do, not what data they edit. Event Storming and similar formats surface these tasks together with the events they cause – see Event-Storming.
  • Ask only for what the task needs. A task has its own small form with the fields that matter for it, not a copy of the whole record.
  • Offer only what is possible. If a book is not borrowed, there is no Return button. The state that the read side shows can tell the interface which tasks make sense right now.
  • Name tasks in the language of the domain. Buttons, commands, and events share their names, so that users, developers, and the history of the system all tell the same story.

Feedback After a Task#

Task-based UIs often run against systems where the read side follows the write side with a short delay. After a user has extended a loan, the list of loans may not show the new due date for a moment. There are several ways to handle this: wait until the read side has seen the new events before showing it again, show the expected result right away, or simply confirm the task and let the view update. Which one fits depends on how quickly the user expects to see the change – see Eventual Consistency in Practice.

Not Everything Is a Task#

Some data really is just data. A reader's preferred language, a book's cover image, the text of a note – editing these as fields is perfectly reasonable, and turning every field into a task would only make the interface harder to use. The question to ask is whether a change has a meaning of its own that the business cares about. If it does, it deserves a task. If it does not, a form is fine.

Interfaces That Tell the Story#

A task-based UI is not primarily a matter of layout. It is a decision to let the interface speak the language of the domain – and in an event-sourced system, that language ends up in the history of the system for good. Designing the tasks is therefore part of designing the domain, and it pays to do it with the same care.