This topic is a bit all over the place, but just wanted to chime in with a 
different approach that I've found works pretty well for updating 
database-backed state, especially in contexts where you aren't syncing with 
the backend via websockets or similar.

That is to follow the principle: no database-backed state gets updated 
directly in the frontend app. Updates are made via *commands *to the 
backend (like an extension of Msgs to the backend, essentially). New state 
is always loaded from the backend, not from a cache (unless you need that 
and can deal with cache invalidation cleanly), preserving the 
unidirectional flow of state.  In this way, you can minimize the need for a 
'store', and never need to directly update data in the 'store' based on 
user input.  I think this may be related to what Kasey was saying 
originally.

Eric

On Sunday, April 30, 2017 at 3:31:49 PM UTC-4, Oliver Searle-Barnes wrote:
>
> The pattern we use is to have our Page.update functions return
>
> (model, cmd, storeCmd)
>
> the main update then applies the storeCmd to the Store. (the actual code 
> supports a few other things but that's the basic gist of it). Hit me up on 
> slack if you want to chat about it.
>
>
>
> On Sunday, 30 April 2017 08:45:49 UTC+2, Dustin Farris wrote:
>>
>> I think I've just had an aha moment with this post.
>>
>> I am in the process of refactoring my monolith MUV into separate modules 
>> with their own MUV for each "page" of my SPA.  Up to this point, I have had 
>> a separate Store module with its own Model and Msg types and an update 
>> function (no view, obviously).  This has worked well up until now, but 
>> after splitting off the pages of my app, it is getting more cumbersome to 
>> update the Store in a way that looks nice.
>>
>> e.g. in my Main.elm I'm ending up with something like
>>
>> update msg model =
>>     case msg of
>>         UserProfilePageMsg msg_ ->
>>             let        
>>                 ( userProfilePageModel, userProfilePageCmd ) =
>>                     UserProfilePage.update msg_ model.userProfilePage
>>             in
>>                 case msg_ of
>>                     UserProfilePage.StoreMsg msg__ ->
>>                         let
>>                             ( storeModel, storeCmd ) =
>>                                 Store.update msg__ model.store
>>                         in
>>                             { model
>>                                 | userProfilePage = userProfilePageModel
>>                                 , store = storeModel
>>                             }
>>                                 ! [ Cmd.map UserProfilePageMsg 
>> userProfilePageCmd
>>                                   , Cmd.map StoreMsg storeCmd
>>                                   ]
>>                     _ ->
>>                         { model | userProfilePage = userProfilePageModel 
>> }
>>                             ! [ Cmd.map UserProfilePageMsg 
>> userProfilePageCmd ]
>>
>>
>> and so on for every page that invokes Store.Msg—which is most pages.
>>
>> I am thinking that there is a better way, and perhaps Kasey's suggestion 
>> of forgoing an in-memory Store on the Model might be it.  I'm still not 
>> sure—I do like the snappy feel of a page loading instantly if the data is 
>> in memory—even if it might change after a brief consultation with the 
>> server.
>>
>> Dustin
>>
>>
>> On Wednesday, April 19, 2017 at 7:28:06 PM UTC-4, Kasey Speakman wrote:
>>>
>>> I'm probably slow, but in recent months I've discovered that trying to 
>>> use Elm's Model like a database or cache (as I have previously seen 
>>> suggested) has turned out to be pretty painful for me. An example 
>>> database-minded model where a section could display *either* a list of 
>>> employees *or* a list of courses.
>>>
>>> type alias Model =
>>>     { employees : List Employee
>>>     , courses : List Course
>>>     , loadingError : Maybe Http.Error
>>>     , route : MyRoute -- employee or course
>>>     }
>>>
>>> The problem this runs into is having to worry about state management. I 
>>> have to remember to "reset" or "turn off" things when they are not active. 
>>> As my application grew, I had a lot of problems that boiled down to tedious 
>>> state management details. My cached data didn't turn out to be all that 
>>> useful because I usually had to reload it anyway, in case something changed.
>>>
>>> Instead, I have been moving toward the model only representing the 
>>> current state of my UI. The big difference here is the model representing 
>>> the current *visual* elements and their data. This leads more to using 
>>> union types to represent parts of the UI. When you switch to a different 
>>> case of the union type, the data from the previous case is *dropped on 
>>> the floor*. This leaves nothing to remember to "reset". RemoteData is a 
>>> good micro-example of this. If there was an error fetching the data, when 
>>> the user requests the data again, you switch back to Loading, the error 
>>> message is dropped on the floor. No forgetting to hide it.
>>>
>>> type RemoteData e a
>>>     = NotAsked
>>>     | Loading
>>>     | Failure e
>>>     | Success a
>>>
>>> If it is really important to cache the data, I prefer to keep that as a 
>>> persistence concern, not on Model. It can be part of the process for 
>>> retrieving the data to first check my chosen cache before making a request 
>>> for fresh data. For instance, first check local storage before making an 
>>> HTTP call. (Currently, this scenario is easier with Native modules for lack 
>>> of Local Storage API or being able to wait on port subscriptions. But it's 
>>> still doable.)
>>>
>>> So working towards a Model reflecting the visuals on the page has been 
>>> an interesting challenge. I'm not claiming it's easier, but so far I've 
>>> found it avoids a class of problems, and has led to some interesting 
>>> discoveries in my own apps. One small example: I realized that my LoggedIn 
>>> and NotLoggedIn routes should actually be separate "apps". Attempts to 
>>> model this in a SPA fashion with the LoggedIn and NotLoggedIn routes as 
>>> siblings always came up with the conundrum: how do I make it a compiler 
>>> error for the model to be in LoggedIn mode but I receive a NotLoggedIn 
>>> message, or vice versa? Even using TEA, I could not avoid this situation. 
>>> Then I realized the only way to do that would be as separate apps. And that 
>>> it was entirely possible to separate them. My "login page" turned out to be 
>>> an entirely self-contained process: the user filling in info, obtaining a 
>>> token, and saving it to local storage.
>>>
>>> I post this in the slim hope it is helpful to someone.
>>>
>>

-- 
You received this message because you are subscribed to the Google Groups "Elm 
Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to