Statecharts - Controlling the behavior of complex systems

By Luca Matteis

  1. 1. Statecharts Controlling the behavior of complex systems Luca Matteis @lmatteis
  2. 2. Recently I’ve been interested in subjects relating the idea of how to make development of software more natural
  3. 3. Specifically in the context of reactive systems
  4. 4. https://vimeo.com/167925763
  5. 5. During this path of research I tried understanding why reactive systems are so complex to develop
  6. 6. What are my recurring pain points during development?
  7. 7. Is it the business logic? Is it that I’m using OO rather than FP? Am I following the right patterns? Is it the language or the tools? Is it the lack of types?
  8. 8. I seem to answer NO to all these questions
  9. 9. Let’s analyze some complex reactive system such as User Interfaces made of different components
  10. 10. Deciding when an event must, may, or must not be triggered seems to be where most of the complexity lies
  11. 11. Orchestrating events Understanding when to trigger an event
  12. 12. For example: We should show the dialog only if the checkbox was clicked and the background color is green and the login component is visible but also nothing should happen if the request is still ongoing
  13. 13. http://www.wisdom.weizmann.ac.il/~harel/ComeLetsPlay.pdf
  14. 14. Recap of paint points: 1) Orchestrating events (aka behavior) 2) Behavior spread all over our app 3) Hard to reason about behavior
  15. 15. Statecharts (in my opinion) provide a way of solving these problems (very biased!)
  16. 16. What is a Statechart?
  17. 17. A statechart takes as input an event, and outputs an action
  18. 18. In other words: you tell a statechart what happened, and it tells you what to do.
  19. 19. Error entry: SHOW_ERROR_DIALOG NoData ShowingData entry: SHOW_DATA_IN_TABLE FetchingData entry: START_HTTP_REQUEST exit: CANCEL_HTTP_REQUEST FETCH_DATA_FAILURE CLICKED_CANCEL / RESET_DATABASE FETCH_DATA_CLICKED FETCH_DATA_SUCCESS Main
  20. 20. It remembers the state it’s in. The active states are identified using a green circle. Main / RESET_DATABASE
  21. 21. Log: FETCH_DATA_CLICKED Main / RESET_DATABASE
  22. 22. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST Main / RESET_DATABASE
  23. 23. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_SUCCESS Main / RESET_DATABASE
  24. 24. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_SUCCESS CANCEL_HTTP_REQUEST SHOW_DATA_IN_TABLE Main / RESET_DATABASE
  25. 25. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_SUCCESS CANCEL_HTTP_REQUEST SHOW_DATA_IN_TABLE FETCH_DATA_FAILURE Main / RESET_DATABASE
  26. 26. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_SUCCESS CANCEL_HTTP_REQUEST SHOW_DATA_IN_TABLE FETCH_DATA_FAILURE FETCH_DATA_CLICKED Main / RESET_DATABASE
  27. 27. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_SUCCESS CANCEL_HTTP_REQUEST SHOW_DATA_IN_TABLE FETCH_DATA_FAILURE FETCH_DATA_CLICKED START_HTTP_REQUEST Main / RESET_DATABASE
  28. 28. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_SUCCESS CANCEL_HTTP_REQUEST SHOW_DATA_IN_TABLE FETCH_DATA_FAILURE FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_FAILURE Main / RESET_DATABASE
  29. 29. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_SUCCESS CANCEL_HTTP_REQUEST SHOW_DATA_IN_TABLE FETCH_DATA_FAILURE FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_FAILURE CANCEL_HTTP_REQUEST SHOW_ERROR_DIALOG Main / RESET_DATABASE
  30. 30. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_SUCCESS CANCEL_HTTP_REQUEST SHOW_DATA_IN_TABLE FETCH_DATA_FAILURE FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_FAILURE CANCEL_HTTP_REQUEST SHOW_ERROR_DIALOG FETCH_DATA_CLICKED Main / RESET_DATABASE
  31. 31. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_SUCCESS CANCEL_HTTP_REQUEST SHOW_DATA_IN_TABLE FETCH_DATA_FAILURE FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_FAILURE CANCEL_HTTP_REQUEST SHOW_ERROR_DIALOG FETCH_DATA_CLICKED START_HTTP_REQUEST Main / RESET_DATABASE
  32. 32. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_SUCCESS CANCEL_HTTP_REQUEST SHOW_DATA_IN_TABLE FETCH_DATA_FAILURE FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_FAILURE CANCEL_HTTP_REQUEST SHOW_ERROR_DIALOG FETCH_DATA_CLICKED START_HTTP_REQUEST CLICKED_CANCEL Main / RESET_DATABASE
  33. 33. Log: FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_SUCCESS CANCEL_HTTP_REQUEST SHOW_DATA_IN_TABLE FETCH_DATA_FAILURE FETCH_DATA_CLICKED START_HTTP_REQUEST FETCH_DATA_FAILURE CANCEL_HTTP_REQUEST SHOW_ERROR_DIALOG FETCH_DATA_CLICKED START_HTTP_REQUEST CLICKED_CANCEL CANCEL_HTTP_REQUEST RESET_DATABASE Main / RESET_DATABASE
  34. 34. Problem: event orchestration Solution: statecharts 1) Single coherent view 2) Blocking events 3) Behavior can be visualized
  35. 35. Problem: event orchestration Solution: statecharts 1) Single coherent view 2) Blocking events 3) Behavior can be visualized
  36. 36. Instead of having the behavior spread across different components we can represent it in a single coherent way
  37. 37. <button
 onClick={() => makeRequest(…)
 }
 > click </button> <DataFetcher
 onResponse={(…) => showTable(…)
 }
 > {children} </DataFetcher> <Scroller
 onScroll={(…) => updateCoords(…)
 }
 > {children} </Scroller>
  38. 38. <button
 onClick={() => transition(‘onClick’)
 }
 > click </button> <DataFetcher
 onResponse={(…) => transition(‘onResponse’)
 }
 > {children} </DataFetcher> <Scroller
 onScroll={(…) => transition(‘onScroll’)
 }
 > {children} </Scroller> NoData onClick / makeRequest Fetching onResponse / showTable ShowingTable onScroll / updateCoords
  39. 39. Problem: event orchestration Solution: statecharts 1) Single coherent view 2) Blocking events 3) Behavior can be visualized
  40. 40. Statecharts also allow us to block events, which is crucial to unexpected behavior
  41. 41. https://medium.com/@asolove/pure-ui-control-ac8d1be97a8d
  42. 42. For any given state, only a specific set of events can be triggered (the arrows going out of a state)
  43. 43. The actions executed in response to an event can vary, depending on the previous interactions the user has had with other components
  44. 44. event-state-action rather than event-action
  45. 45. Problem: event orchestration Solution: statecharts 1) Single coherent view 2) Blocking events 3) Behavior can be visualized
  46. 46. Using Statecharts in real-world apps
  47. 47. const statechart = { initial: 'Main', states: { Main: { on: { FETCH_DATA_CLICKED: 'FetchingData', }, initial: 'NoData', states: { ShowingData: {}, Error: {}, NoData: {} } }, FetchingData: { on: { FETCH_DATA_SUCCESS: 'Main.ShowData', FETCH_DATA_FAILURE: 'Main.Error', CLICKED_CANCEL: 'Main.NoData', }, onEntry: 'FETCH_DATA_REQUEST', onExit: 'FETCH_DATA_CANCEL', }, } } Main
  48. 48. const statechart = { initial: 'Main', states: { Main: { on: { FETCH_DATA_CLICKED: 'FetchingData', }, initial: 'NoData', states: { ShowingData: {}, Error: {}, NoData: {} } }, FetchingData: { on: { FETCH_DATA_SUCCESS: 'Main.ShowData', FETCH_DATA_FAILURE: 'Main.Error', CLICKED_CANCEL: 'Main.NoData', }, onEntry: 'FETCH_DATA_REQUEST', onExit: 'FETCH_DATA_CANCEL', }, } }
  49. 49. const statechart = { initial: 'Main', states: { Main: { on: { FETCH_DATA_CLICKED: 'FetchingData', }, initial: 'NoData', states: { ShowingData: {}, Error: {}, NoData: {} } }, FetchingData: { on: { FETCH_DATA_SUCCESS: 'Main.ShowData', FETCH_DATA_FAILURE: 'Main.Error', CLICKED_CANCEL: 'Main.NoData', }, onEntry: 'FETCH_DATA_REQUEST', onExit: 'FETCH_DATA_CANCEL', }, } } statechart(‘CLICKED_CANCEL’) // No actions statechart(‘FETCH_DATA_CLICKED’) // FETCH_DATA_REQUEST statechart(‘FETCH_DATA_CLICKED’) // No actions statechart(‘FETCH_DATA_SUCCESS’) // …
  50. 50. xstate https://github.com/davidkpiano/xstate
  51. 51. Redux middleware https://github.com/lmatteis/redux- statecharts
  52. 52. const statechart = { initial: 'Main', states: { Main: { on: { FETCH_DATA_CLICKED: 'FetchingData', }, initial: 'NoData', states: { ShowingData: { onEntry: ‘SHOW_DATA’, }, Error: {}, NoData: {} } }, FetchingData: { on: { FETCH_DATA_SUCCESS: 'Main.ShowData', FETCH_DATA_FAILURE: 'Main.Error', CLICKED_CANCEL: 'Main.NoData', }, onEntry: fetchDataRequest, }, } }
  53. 53. const statechart = { initial: 'Main', states: { Main: { on: { FETCH_DATA_CLICKED: 'FetchingData', }, initial: 'NoData', states: { ShowingData: { onEntry: ‘SHOW_DATA’, }, Error: {}, NoData: {} } }, FetchingData: { on: { FETCH_DATA_SUCCESS: 'Main.ShowData', FETCH_DATA_FAILURE: 'Main.Error', CLICKED_CANCEL: 'Main.NoData', }, onEntry: fetchDataRequest, }, } } function fetchDataRequest() { return (dispatch) => { fetchData() .then(() => dispatch({ type: FETCH_DATA_SUCCESS, }) ) .catch(() => dispatch({ type: FETCH_DATA_FAILURE, }) ) } }
  54. 54. const statechart = { initial: 'Main', states: { Main: { on: { FETCH_DATA_CLICKED: 'FetchingData', }, initial: 'NoData', states: { ShowingData: { onEntry: ‘SHOW_DATA’, }, Error: {}, NoData: {} } }, FetchingData: { on: { FETCH_DATA_SUCCESS: 'Main.ShowData', FETCH_DATA_FAILURE: 'Main.Error', CLICKED_CANCEL: 'Main.NoData', }, onEntry: fetchDataRequest, }, } } function fetchDataRequest() { return (dispatch) => { fetchData() .then(() => dispatch({ type: FETCH_DATA_SUCCESS, }) ) .catch(() => dispatch({ type: FETCH_DATA_FAILURE, }) ) } } function fetchDataReducer(state, action) { switch(action) { case ‘SHOW_DATA’: return { …state, data: action.payload } default: retrun state } }
  55. 55. When we convert the designs (specs) into code we lose the high- level understanding of our app
  56. 56. Too far apart Not connected
  57. 57. “Curiosity is one of the most ambitious and successful NASA missions. It is on Mars now. But not everyone knows that the generated code has been part of Curiosity’s Rover flight software since launch, and continues to run on board today. Code generated from state chart diagram is used for: auto-maneuver (Cruise phase), Spacecraft Modes (Configures the spacecraft), Launch mode, Cruise mode, entry, descent, landing, and rover mode.” https://blog.nomagic.com/statechart-autocoding-curiosity-rover/
  58. 58. Harel, D. (1988). On visual formalisms. Communications of the ACM, 31(5), 514-530.
  59. 59. If Statecharts are so great, why haven’t they taken off yet?
  60. 60. If Statecharts are so great, why haven’t they taken off yet? * High barrier to entry * Code generation
  61. 61. What about all other types of diagrams?
  62. 62. What about all other types of diagrams? * Each solve a different problem * Statecharts are concerned with event orchestration and reactivity
  63. 63. Thanks!