How would Mobius run a dosa restaurant?

5 minute read
Janahvi

Our decision to migrate the architecture for Simple, one of the apps we have helped build over the last 3 years, was not easy. Our ADR here captures most of why we had to migrate and how did we decide on Mobius. In short, it solved one of the biggest challenges faced by front-end apps–state management.

Mobius is a functional reactive framework for managing state evolution and side-effects. It emphasises on a clear separation of concerns, testability, and an immutable state for idempotency. It’s important to understand how Mobius works in order to appreciate the simplicity of the framework.

And therefore, taking a page from Jay’s article who explains Redux using a donut shop, we thought it’d be a great idea to explain Mobius through a different lens and see how it would fix the chaotic management of a dosa restaurant and eventually your app.

Presenting to you, Mobius Dosa Shop!

Imagine that you discover a new dosa restaurant around the corner that serves customisable dosas. As someone who loves food and is super curious, you visit that place for lunch.

You place an order for a Rava Masala Dosa and a Mysore Masala Dosa. The waiter takes your order to the kitchen counter.

After a few minutes, the waiter comes back to your table with a Masala Dosa and a Plain Dosa.

“But, that is not what I ordered?!” you ask the waiter.

To which the waiter just says that “I don’t know how this happened. My job is to give the order in the kitchen counter and bring what they give back.”

You realise that the waiter is right and dig a bit deeper into why this happened by talking to the Head Chef and the Restaurant Manager.

After multiple rounds of asking questions and poking and prodding, you realise that the place is a mayhem.

What was going wrong at Mobius Dosa Shop?

  1. Inventory management : Proper records for batter portions weren’t maintained, which meant that the chefs surprisingly ran out of batter quite often
  2. Kitchen coordination: There was no proper allocation of work among all the chefs
  3. Bad menu management: Neither was the menu correctly updated with in-stock items, nor was the waiter aware, and this confusion led to unfulfilled dosa orders.

Overall, the kitchen and restaurant operations severely lacked proper management and communication.

So you decide to help them out and propose a detailed solution.

Mobius Dosa Shop v2.0:

Bring in Laxman—The Mobius Dosa Shop Ninja!

  • Laxman acts as the middle man between the Waiter and the Chef taking orders for smoother communication

Tackle bad menu and inventory management:

  • Laxman separates the dosa batter into measured portions and updates the order count of the batter
  • Instead of printed menus you introduce blackboards for updating menu in real-time
  • The Waiter keeps a track of the inventory and updates the menu on the blackboard regularly

Improve kitchen coordination:

  • The Chef handles prepping of the dosas end-to-end
  • Laxman gets the order from the waiter and sends it to the Chef
  • Once the order is prepared, Laxman gives it to the Waiter so he can serve the customer, and updates the inventory count of the batter portions left

Let’s represent the above solution using a state diagram

and put what’s happening in the state diagram in code.

Image111

Mobius Dosa Shop Initial State

After the shop is opened, Laxman fills the dosa batter portions with say, 10 portions.

override fun update(
    model: MobiusDosasModel,
    event: MobiusDosasEvent
): Next<MobiusDosasModel, MobiusDosasEffect> {
  return when (event) {
    isOrderPlaced -> orderPlaced(event.dosa, model)
  }

  private fun orderPlaced(
      dosa: Dosa,
      model: MobiusDosasModel
  ): Next<MobiusDosasModel, MobiusDosasEffect> {
    valupdatedModel = if (dosa.batter == RICE) {
      model.reduceRiceBatterPortion()
    } else {
      model.reduceRavaBatterPortion()
    }

    return next(updatedModel, setOf(PrepareDosa(dosa)))
  }

The waiter freshly fills the board with all the menu items.

override fun render(model: MobiusDosasModel) {
  if (model.hasRiceAndRavaDosaBatter)
    ui.writeAllMenuItems()
}

Dosa Preparation State

The Waiter places an order by telling it to Laxman. Laxman updates the dosa batter portion and asks the Chef to prepare the dosa.

override fun update(
    model: MobiusDosasModel,
    event: MobiusDosasEvent
): Next<MobiusDosasModel, MobiusDosasEffect> {
  return when (event) {
    isOrderPlaced -> orderPlaced(event.dosa, model)
  }

  private fun orderPlaced(
      dosa: Dosa,
      model: MobiusDosasModel
  ): Next<MobiusDosasModel, MobiusDosasEffect> {
    valupdatedModel = if (dosa.batter == RICE) {
      model.reduceRiceBatterPortion()
    } else {
      model.reduceRavaBatterPortion()
    }

    return next(updatedModel, setOf(PrepareDosa(dosa)))
  }

Dosa Prepared State

When the dosa is successfully prepared, the Chef updates Laxman, who in turn asks the Waiter to serve the dosa to the customer.

DosaPrepared -> dispatch(setOf(ServeTheDosa))

The Menu Board’s Status

The Waiter continues checking the dosa batter portion size with Laxman. As soon as he finds that the batter for a particular dosa is finished, he removes that item from the menu.

if (model.isRiceBatterPortionFinished) {
  ui.removeRiceDosaFromMenu()
}

if(model.isRavaBatterPortionFinished) {
  ui.removeRavaDosaFromMenu()
}

All the information transferred between Laxman, the Waiter, the Chef, the Menu board, and the dosa batter inventory can also be represented using a loop here:

AAA

The dosa restaurant loop ends up looking a lot like what we are trying to understand here, the Mobius Loop.

Mobius&#x20;diagram&#x20;1

A Mobius loop receives Events, which are passed to an Update function. As a result of running the Update function, the Model might change, and Effects might get dispatched. The Model can be observed by the user interface, and the Effects are received and executed by an Effect Handler.

Let’s understand this in terms of our analogy:

The dosa restaurant loop receives events like OrderPlaced or DosaPrepared which are passed to Laxman. As a result, Laxman either updates the dosa batter portion and asks Chef to PrepareDosa or asks Waiter to ServeTheDosa. The Waiter observes the dosa batter portions and based on the observations updates the menu board, and Chef/Waiter receive commands from Laxman which are either PrepareTheDosa or ServeTheDosa.

Conclusion

In conclusion, Mobius is a loopy architecture with excellent separation of concern that enables unidirectional flow between various stakeholders.

To get a quick start on implementing a screen using Mobius, look at any <screen-name>UpdateTest classes in the Simple codebase. An easy starting point is [CriticalAppUpdateDialog]which shows a dialog reminder to users to update their app.

After understanding the basics and capturing the beauty of thinking about your code the functional reactive way, here are a few sources you can look at to learn and experiment more on Mobius and functional reactive programming:

Header illustration by Ranganath Krishnamani and Mobius Loop image by Spotify.