impierce / identity-wallet

A Tauri-based Identity Wallet for people to manage Decentralized Identities and Verifiable Credentials.
https://www.impierce.com
Apache License 2.0
18 stars 3 forks source link

Query handlers #110

Closed daniel-mader closed 1 month ago

daniel-mader commented 7 months ago

Description

In the current redux-like design, we have a "single point of communication" through a single generic Tauri command: handle_action(). This allows for easily testable state updates. However, querying data from the backend is not part of this design since all data that the backend holds in its state is always transported fully to the frontend. As the amount of data is growing (connections, credentials, history) it might make sense to not send all data to the frontend, but allow the frontend to query the backend in some form. There are two ways to design this (possibly more):

  1. Add a separate query() command and possibly also a separate listener for the frontend to make use of the response. This introduces a bit more complexity for the frontend as it would need to know what and where to listen for (temporary "oneshot" listener for just one single query?). This would allow the frontend to fetch data once, then handle it internally without any persistence.
  2. Extend the current design (single command) and introduce "query actions" that are also handled by reducers, but not all data that the backend holds is put into the state for the frontend. This is generally more "async safe" as it follows the redux-like loop of data flow.

Motivation

We have to tackle growing amounts of data while (hopefully) introducing only little additional complexity.

Requirements

  1. Allow the frontend to display large amounts of data (such as lists) without having to hold them at all times.
  2. Make more specific requests for data that could be computationally intensive (better handled by Rust than by JavaScript).

Open Questions

We can make the assumption that the amount of data will still be reasonable, even for heavier usage (20+ credentials, etc., 100+ connections) as it is purely text. We need to consider if we really want to handle the additional complexity of having to handle a separate "frontend state" and a "backend state" or if we decide to stick with "one state for frontend and backend" and solve the querying through the backend when necessary, so the frontend and backend always have the same view on data at all times.

Are you planning to contribute this in a PR?

Yes

Oran-Dan commented 6 months ago
  1. ...
  2. Extend the current design (single command) and introduce "query actions" that are also handled by reducers, but not all data that the backend holds is put into the state for the frontend. This is generally more "async safe" as it follows the redux-like loop of data flow.

I like this option combined with only emitting features, and then perhaps only have a persistent "all-data" state in the backend. The backend shoud hold at least all the data from the stronghold, since we don't want to trigger that one all the time. Then we can have queried lists of the data in the feat_states which is something similar to what we have in the user_data_query as we have it now. But then instead of only a list of UUID's (not the complete object) and then matching them against the full data in the frontend and showing them, we can take away the full data for the frontend and send the queried list only but as complete objects (e.g. credentials). Then there wouldn't even have to be an extra listening point, we would only have to get the initial action payload of opening a new tab correctly configured: Show the initial data, and all data afterwards would either be scrolling down (lazy load) or an actual query of sorts right? I get that a separate listening point is probably even slicker and nicer but as you said it will add some complexity perhaps and the above solution might avoid that.

daniel-mader commented 6 months ago

We should also explore if it generally makes sense to have a different state for frontend and backend at all. We are in a "near-instant" environment where data transfer from backend to frontend can be regarded as a regular function call as if it were in the same environment. There is no potential delay (network) in between and I am also not sure if we should even consider it to be two different things. I really do not want to end up in some "query hell" where we fetch only IDs for some data and lazy load some other data but only if condition XYZ is met and then we might also want to cache some data in the frontend in case the user wants to visit that route again. I absolutely agree that we need to "offload" some data in the backend that shouldn't be part of the state at all times (in a local storage solution), but then we could still have one state for both backend and frontend. Does that make sense?

Oran-Dan commented 6 months ago

I get most of what you're saying. If I understand the tauri communication between back and front correctly, we can simply filter what send to the front end, correct? This would then enable keeping storage in the backend appstate. Furthermore, emitting only updated fields to the front end would then already solve most of what we're trying to do here I think. Considering the queries, we could then have just one queried field in the appstate from which the front end can read. The queries can be simple actions just as it is now and no further complexity would be needed.