You’re not wrong… In fact… your suggesting what this service pattern does:
Consider a normal Elm style system where a top component has two independent sub-components:
top
/ \
a b
Top holds the state of the sub-components, passing it to them in update
, and then taking it back, along with low level operations (Cmd
) to be performed.
Now, consider if both sub-components need to access a shared resource - say a MIDI connection, or perhaps an session with a back end:
top
/ \
a b
\ /
service
We could pass the service’s state to a and b each time they are invoked, and have them pass the service’s state to the service, and then pass the updated service state back to top, along with their own… but you can see how this gets messy quickly, especially if there are multiple services and multiple layers.
To make matters worse, if the service needs access to ports, this must be plumbed all the way to the top, and responses back down. Again doable, but the type signatures for update
in a and b are starting to get quite unwieldy.
But the final stumbling block is that the service in that position can’t do things that a pair of in/out ports can: It can’t offer a module like a the ability to request something, passing a message function so that a msg will be sent back to a’s update function when done.
Now consider your first point: You’d look to moving the internal structure (service) upward into top. This is fine and solves some of the problems… but not when there are half a dozen services or several layerings… they’d all end up in top, which is unworkable.
… But your intuition is right: service shouldn’t be “under” the other modules, because in a functional pure system, it can’t easily be there due to the way state is handled.
Which is where the service pattern comes in: The service (or services) can be their own things without cluttering up top, but still offer services to sub components a and b. What’s more, the interaction follows “the Elm way”: a requests service by returning a Cmd
like object from update
… and gets a response later via a Msg
.
We end up with:
top
/ | \
a | b
|
service
Where communication is mediated by top, in a completely type safe way, and without top having to add code for every function in the service.