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 elm-discuss+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to