Refactor State Service
Context and Problem Statement
This ADR builds upon Adopt Observable Data Services for Angular.
The Bitwarden clients currently have a quite complex state architecture, where all the state is handled by a single service. This has resulted in everything being tightly coupled to the StateService
essentially making it a God object.
Additionally any service or component can directly access any state using the state service. Which makes it difficult to follow the state lifecycle of each data type, and introduces uncertanty in how the data is accessed.
Decision Outcome
We should refactor the state service to be a generic storage container.
- Good: Eliminates the “good” functionality of the state service
- Good: State is maintained by the service which owns it.
- Good: No arbitary access of data.
- Bad: Brings back arbitary keys that must be unique.
Example
interface StateService {
getAccountData<T>: (account: Account, key: string, options?: StorageOptions) => Promise<T>;
saveAccountData: (account: Account, key: string, options?: StorageOptions) => Promise<void>;
deleteAccountData: (account: Account, key: string, options?: StorageOptions) => Promise<void>;
deleteAllAccountData: (account: Account);
getGlobalData<T>: (key: string, options?: StorageOptions) => Promise<T>;
saveGlobalData: (key: string, options?: StorageOptions) => Promise<void>;
deleteGlobalData: (key: string, options?: StorageOptions) => Promise<void>;
}
// StorageKey is an internal constant, and should be prefixed with the domain.
// DO NOT EXPORT IT.
const StorageKey = "organizations";
class Organization {
async save(organizations: { [adr: string]: OrganizationData }) {
await this._stateService.saveAccountData(this._activeAccount, StorageKey, organizations);
await this._organizations$.next(await this.decryptOrgs(this._activeAccount, organizations));
}
}