Hi. I want to expose here an analysis I’ve been doing of a use case I have at work, and the conclusions I’ve taken. To be honest, it started as an open question, but while explaining I ended up finding a solution that feels right for me. But I still want to share it and validate the conclusion with the community.
The simplified version of my use case is the following:
- An SPA application with several views
- In the application there are views that are only a function rendering some data (like checkbox example). There are also views that have internal state and specific Msgs that mutate such state (like elm-sortable-table)
- The application needs to be able to manage different kind of assets. Depending on the type of asset managed, the views might have a different representation, different inputs and a different behaviour (ie have a different model, different Msgs, different view). It can be reduced to: the views can have multiple versions
- All views must use the same version, therefore the knowledge of which version is used must be part of the Model of the root view.
- Only one version of each view will exist at a time, therefore it feels like we should only hold the Model of one of the versions at any given time (
Model = V1.Model | V2.Model
). This is my understanding of “make impossible states impossible”, although I might be misunderstanding it.
The problem is: Following the standard Elm Architecture pattern, premise 5) cannot be achieved without compromising the consistency of the state of the application.
I have some example code.
- Initial scenario with the consistency of the model broken
-
Attempt 1: Ignore premise 5) and store the model of each version of the view separately
- Works fine, but breaks 5) and is not scalable in the long term
-
Attempt 2: stateless view, all versions implemented in a single module
- It puts what feels wrong of the previous solution to the extreme. It’s messy, as it have unrelated things implemented together (don’t get fooled by the simplicity of the example), and does not scale.
-
Attempt 3: Maintain the consistency of the Model “manually”
- This option is an straightforward patch so that the consistency of the model does not break in practice, although it is still broken from a theoretical point of view.
-
Final Solution: each view sends back to the parent its own state in toMsg config
- This is my understanding of the pattern implemented in Evan’s Sortable table. However, while Evan’s suggests that having the update hidden inside the config is just a matter of simplification of the API, my conclusion is that it is in fact the only option to maintain the consistency of the views of this use case, thanks to the fact that the view is able to apply its own update inline in the event and send back to the client code the already updated version of the model, without relying on indirections.
Now, my questions are:
- Does anybody disagree that the final solution is indeed, the only solution? Would you solve this use case differently?
- In the code of the final solution, I’ve deliberately leaved the function update untouched and called it on the onClick event, to show the difference with the standard pattern of The Elm Architecture. Does this feel as an antipattern to anybody?
- Is this a valid evidence to support the case that the standard elm architecture pattern is only valid for the root module of the application, and an anti-pattern for subviews?