Auto Save Architecture
Goal
The purpose of this document is to outline an approach for building an auto-save feature in a web application. This feature allows data updates to be automatically saved to the database when a user stops typing. Each entry’s validity is verified, ensuring that only valid data is saved, while also tracking changes to prevent redundant save operations.
Overview of the Solution
Auto-save functionality involves:
- Detecting when the user pauses typing and triggering a save action.
- Validating each data entry before saving.
- Managing UI states to track if data is synced, being saved, or needs to be saved again.
Technical Requirements
Each row in a data table represents a unique entry, requiring:
- Automatic saving when a user pauses typing for a specific duration.
- Validation for each entry before it’s sent to the API.
- The ability to add or delete rows as needed.
Core Properties and Terminology
Property | Purpose |
---|---|
guid | A unique identifier for each row, used for UI tracking and syncing entries with the database. |
isValid | Indicates if an entry passes validation. Only valid entries will be saved. |
isSynced | Tracks if an entry’s data has been saved to the database. |
isSavingInProgress | Indicates if a save operation is currently underway for a specific entry. |
These properties are fundamental to effectively managing each entry's save state.
Data Entry Save Conditions
For a row (referred to as DataEntry
) to be saved, it must meet these conditions:
- isValid = true: Entry passes validation.
- isSynced = false: Data has been modified since the last save.
- isSavingInProgress = false: There’s no ongoing save request for the entry.
A memoized selector aggregates rows that match these conditions. Subscribing to this selector triggers save requests, optimizing performance by only saving necessary entries.
Life Cycle of a DataEntry
The lifecycle below illustrates how a DataEntry
moves through the auto-save process. Each step aligns with the required application state changes, ensuring data consistency and minimizing redundant saves.
Step 1: Load Data on Page Load
Upon application start, a request fetches all data entries from the database. Each entry receives a guid
to uniquely track it in the UI.
Step 2: Initial Validation of Data
Once data is loaded, the application validates each entry, marking isValid
as true
or false
to indicate validity and provide feedback if any issues are found.
Step 3: Data Ready for Interaction
The application waits for user interaction. No save requests are triggered at this point.
Step 4: User Modifications
As a user modifies a DataEntry
, Redux updates the entry’s state, setting isSynced = false
and adjusting its validity.
Step 5: Auto Save Trigger
After user input, if no changes are detected for a specified duration (debounced at 500ms), an auto-save action triggers, targeting rows that meet the save conditions.
Step 6: Auto Save Request Sequence
When the save action dispatches, an API call is triggered to store these entries in the database. Below is an effect handling the save request and dispatching a response upon completion.
Step 7: Update Save Status
When SAVE_RESPONSE_TABLE_ROW_ENTRIES
is dispatched, the reducer updates isSavingInProgress
to false
, and, if no further changes were made during the save, sets isSynced = true
. If new changes occurred during the save, it returns to Step 5.
Error Handling
If a save fails, retry the save or notify the user. Ensuring each failed entry is re-saved upon the next valid state change helps maintain data consistency.
By following these steps, you can implement a robust auto-save feature that reduces redundant saves, ensures data integrity, and enhances the user experience with real-time data updates similar to Google Sheets.
Written by Roman Khrystynych who lives and works in Toronto building interesting things.