A RealWorld example app version to demonstrate how to structure an Elm application using the Effect pattern.
The application is initially based on the great rtfeldman/elm-spa-example.
Introduction
Most of complex Elm application architecture examples out there use some variant of the pattern used in rtfeldman/elm-spa-example or the OutMsg pattern to have a updatable global state available in each subpage (a Session
for example). The former passes the data from subpage to subpage during their initialization, so each subpage can modify it locally, the later returns an additional message from their update
function handled in the top level one.
My favorite pattern to do this (and more), the Effect pattern, is most often described only in the context of tests and Cmd
, for example by avh4/elm-program-test, but it can be extended to state changes and offers a clean and versatile way to handle different use cases of complex applications.
Description
The extended Effect pattern used in this application consists in definining an Effect
custom type that can represent all the effects that init
and update
functions want to produce.
These effects can represent:
- a
Cmd
value - a request to change the state at an upper level (for example an URL change from a subpage without an anchor)
Having a way to request an upper level state change, with or without an associated actual Cmd msg
, is what lets this extended effect pattern combine the benefits of the OutMsg
one with the ones from the usual effect pattern used for tests.
The init
and update
functions are changed to return a (Model, Effect msg)
, using a custom application
, that will turn the Effect
info actual effects through a perform
function.
There are several benefits to this approach that makes it a valuable pattern for complex applications, including:
-
All the effects are defined in a single
Effect
module, which acts as an internal API for the whole application that is guaranteed to list every possible effect. -
Effects can be inspected and tested, not like
Cmd
values. This allows to test all the application effects, including simulated HTTP requests. -
Effects can represent a modification of top level model data, like the Session when logging in, or the current page when an URL change is wanted by a subpage
update
function. -
All the
update
functions keep a clean and conciseMsg -> Model -> ( Model, Effect Msg )
signature. -
Because
Effect
values carry the minimum information required, some parameters like theBrowser.Navigation.key
are needed only in the effectsperform
function, which frees the developer from passing them to functions all over the application. -
A single
NoOp
orIgnored String
can be used for the whole application.
Differences with elm-spa-example
To focus on the architecture changes and to offer a different perspective, the elm-spa-example has been quite heavily reworked with the following changes:
- Refactored to use an Effect type and application
- Upgraded dependencies, including
elm/http
to 2.0 - Removed client-side fields validation to focus subpage code on effects
- Put all specific styled view code into a single
View
module to declutter subpages code - Replaced slow loading detection by a simple CSS transition
- Refactored according to personal preferences
- Removed custom assets
Links
-
Testing programs with Cmds with
avh4/elm-program-test
. - A service pattern: @mzero explored a variant of this pattern to implement application wide services.
- The Effect pattern: Transparent updates in Elm.
Credits
Refactoring the elm-spa-example
from Richard Feldman made me realize how much work went into it.
I would most likely never had released this version without such a solid base, so thank you very much @rtfeldman.