Link Search Menu Expand Document
ID
ADR-0004
Status
In progress
Published
2022-06-30

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));
  }
}