Relm Interceptors: a proposal for data store support (and data store-like things) #498
chriskilding
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Many apps need to read or write state from an external location in order to be useful. This external location could be, for example, a remote Web API, or a database, or a local file on disk.
This is a proposal for a Relm feature that could support data stores - and other things which behave like them.
At this point I am just putting some ideas out there to get the ball rolling; this is by no means the final state of the feature.
Considerations
We must account for the following requirements in this feature.
Data model != View model
The data model (the structure of the state that is read/written) may differ significantly from the view model (the Relm4 Component hierarchy in your app, or the
Self::Init
types within Components). Therefore, any data store feature for Relm must be able to translate between the two.Separation of concerns
Relm4 Components should not have direct knowledge of the data store. Among other things, this means the data store class should not appear as an instance variable or method on components (or be passed within a Component's
Self::Init
). Essentially it should be possible to attach or detach the interceptor (data store functionality) to your Relm app without your Relm views knowing.Event-based updates
Traditional data stores (such as local files) can be written to in a monolithic manner (prepare entire data model in-memory and serialize the whole thing when 'Save' is invoked), but this is not efficient for large files. Newer data stores, such as real-time Websockets APIs, cannot be used this way at all; they are event-based. Therefore, any data store feature for Relm should have an event-based update API.
For example, if I have a to-do app with the following data model, if I update the text of a
Todo
within aList
, I should be able to issue a delta update in the backend for just that individual changed item (e.g. an HTTP requestPATCH /lists/1/todos/2
):Proposed solution
I propose adding a new "interceptor" feature to Relm4.
The interceptor should be able to intercept any
Input
event within the view model hierarchy that it's attached to.For example, in the to-do app view model shown above, if I attach the interceptor at the
Lists
level, it should be able to interceptListsInput
ListInput
andTodoInput
events. Meanwhile if I attach it at theList
level, it should only interceptListInput
andTodoInput
events.The interceptor should (somehow) delegate to existing Relm features for async work (such as
Worker
), to handle the actual interaction with the backend.In other words, Relm4 already has Components (with events) and Workers (to do the data store I/O); the interceptor is there to plug the two together.
Benefits
In addition to satisfying the requirements above, the interceptor feature has the following benefits.
<Foo>Updated
type events), batch multiple update events together, then sync the update stream to the backend periodically.Open questions
Reading plus writing (and preventing cycles)
The proposal so far mainly covers how an interceptor would support writes to a data store (view model -> data model). We also need it to support reads (data model -> view model). If we want the data -> view updates to use the existing Relm Component input events mechanism, the interceptor will need to know the originator of an event. This is to prevent infinite cycles of reading and writing.
In the example above, say that you have an interceptor that reacts to
TodoInput
events. When theTodoInput::UpdateText
event occurs, it should write the new value to the data store. When a change occurs in the data store, the interceptor should issue aTodoInput::UpdateText
event back to the view. Left unchecked, this would create a cycle. But if the interceptor knows that it was the originator of theTodoInput::UpdateText
event, it can then decide to stop further processing, and not write that event's value to the data store. This breaks the cycle.Using interceptors in unit/integration tests
Another question is whether we could use the interceptor in unit/integration tests for a Relm app. This would allow us to observe events that are going on within the app. It could also allow us to raise fake events for the test.
Handling failures
Attached interceptors might fail to handle an event.
Sometimes we don't care about the failure (e.g. if a log line doesn't get written, that's probably not critical), so we can just let that happen silently.
But sometimes we do care about the failure (e.g. if the app's remote API is down, and after retrying-with-backoff the app still can't contact it), and we might need to alert the user or prevent further processing. The interceptor feature will need a mechanism to do this.
Beta Was this translation helpful? Give feedback.
All reactions