DEV Community

Discussion on: One year of Elm in production

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

Probably the biggest mistake was in how we designed the Model. We tried to use it as a "database". So for example, when we switched to a page, we would load the data for that page into a property on the model. Then we would reuse the data on another page. However, this led to a lot of code just to check/maintain this shared data. And it was the source of many state-based bugs. (One page left the data in X state, but another page expected it in Y state. Or one page needed field Q but the other page didn't for its view.)

Ultimately, our use cases required the app not be offline-capable and our users really preferred getting the latest data from the API any time they switched pages. So we dropped this shared state model. We went to a strategy where the model represents the state of UI elements. So, pages are part of a DU (aka custom type) because only one can be shown at a time. So when the user switches pages, all the data from the other page is gone. No need to reset it or make sure it is left in a certain state.

So in the end, our model ends up looking more like this. Code is off the top of my head.

type alias Model =
  { apiToken : String
  , currentPage : Page
  }


type Page
  = Home
  | OrderView OrderViewState
  -- others like OrderList

type alias OrderViewState
  { orderId : String
  , orderData : Remote OrderData
  }

type Remote a
  = Loading
  | LoadFailed Http.Error
  | Loaded a

The way we initially did state also led to weird extra routing types. Those became unnecessary once we switched to this model. It maps very cleanly to/from navigation URLs. It is easy to parse the URL /order/{orderId} and convert it into the OrderView page:

let state = OrderViewState orderId Loading
    page = OrderView state
in { model | currentPage = page }
   ! [ Api.getOrderData model.apiToken orderId ]

Going the other way is easy too.

case model.currentPage of
  OrderView { orderId } ->
    "/order/" ++ orderId

Anyway, I think modeling as a database was the main thing that we did wrong at first.

Thread Thread
 
ok32 profile image
ok32

Got it. Thank you!