vue+akitaでのステート設計パターン

# [Akita](https://github.com/datorama/akita) とは

[Akita](https://github.com/datorama/akita)はDatoramaというセールスフォースに買収されたマーケティング会社に開発されてるOSS。
Rxjsで作られた、Angular,React,Vueなどでのステート管理ライブラリ。

流行りのステート管理にはflux実装のReduxやvuexなどあるけれど、正直言って登場人物が多すぎて冗長過ぎる。だからmobxとかもあるけど、それならもはやakitaでいいんじゃないかって気がする。

※ちなみにakitaはchrome extensionのredux-devtoolで状態を追える。
[詳しいコンセプトはこちら](https://netbasal.com/introducing-akita-a-new-state-management-pattern-for-angular-applications-f2f0fab5a8)

## Store
Akitaには２種類のStoreが存在する。

### Store
Storeはストアの状態を管理する単一なObjectなどを入れるところ。基本的にはコレクションなどは入れるべきではない。
提供されるAPIはsetLoading,setError,destroyのみ。

### EntityStore
EntityStoreはデータなどを入れるとCollectionとして管理してくれる。
こちらは特殊でsetLoading,setError,destroyは当然提供されてますが、コレクションの管理もしてくれます。

e.g.

```ts

import { EntityState, EntityStore, StoreConfig } from '@datorama/akita';

type User = {
  id: number;
  name: string;
};

interface UsersState extends EntityState<User> {}

@StoreConfig({ name: 'users' })
class UsersStore extends EntityStore<UsersState, User> {
  constructor() {
    super();
  }
}

const usersStore = new UsersStore();

const users = [{ id: 1, name: 'name 1' }, { id: 2, name: 'name 2' }, { id: 3, name: 'name 3' }]
userStore.add(users);
```
とすると、こんな感じでEntityStoreが管理してくれます。

```ts
{
  users: {
    entities: {
      '1': {
        id: 1,
        name: 'name 1'
      },
      '2': {
        id: 2,
        name: 'name 2'
      },
      '3': {
        id: 3,
        name: 'name 3'
      }
    },
    ids: [1, 2, 3],
    loading: false,
    error: null
  }
}
```

## Store/EntityStoreへのアクセス

本家より拝借したakitaアーキテクチャーイメージ
![1_ZvboOQwyeAjPVKdYmaA1dA.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87900/8349b41b-2431-b044-3e76-d7c53617b9db.png)
登場人物は以下の３種類。

### Service
Storeを更新する。
ComponentはServiceのIFを意識するだけ。
### Store
上の方で説明済み。
### Query
Storeからデータを取得する。Observableにデータの取得ができる。

基本的には以下の流れ。

1. ComponentからServiceを介してStoreを更新
2. ComponentからQueryでStoreを取得

# normalizr+reduxのようなステート設計

ここではredux+normalizrのようなentityとidを分けて保管して、表示するidだけを他で管理するようなUser一覧ページを作ってみる。
- [Normalizrを使用したReduxの実装パターン](https://qiita.com/hiyamamoto/items/f1f86e94be083526d465)

細かいところはGithubに上がってるので、ソースコード読んでほしい。

１. UserListコンポーネントは表示する情報を管理するこんなStoreを持つ。

```ts
import { Store, StoreConfig } from '@datorama/akita';

export interface UserListState {
  paging: {
    page: number;
    total: number | null;
  };
  ids: Array<number>;
}

export function createInitialState(): UserListState {
  return {
    paging: {
      page: 1,
      total: null
    },
    ids: []
  };
}

@StoreConfig({ name: 'userList' })
export class UserListStore extends Store<UserListState> {
  constructor() {
    super(createInitialState());
  }
}

export const userListStore = new UserListStore();
```
**redux-devtool**
<img width="195" alt="Screen Shot 2019-12-13 at 20.37.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87900/ce47ec86-e0fd-7c08-5f39-20d5094a2e09.png">

２. APIの結果となるUser情報を管理するEntityStoreをsharedに持つ。
APIで情報を取得する度にここに蓄えた後、上記のStoreも更新する。

```ts
import { EntityState, EntityStore, StoreConfig } from '@datorama/akita';
import { User } from './user.model';

export interface UsersState extends EntityState<User> {}

@StoreConfig({ name: 'users' })
export class UsersStore extends EntityStore<UsersState, User> {
  constructor() {
    super();
  }
}

export const usersStore = new UsersStore();
```
**redux-devtool**
<img width="372" alt="Screen Shot 2019-12-13 at 20.39.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87900/6225d24b-f7ff-2345-41f1-599600cab8e2.png">


3.Componentでは1のStoreをwatchして、画面に表示するUserListを決定して更新する。

#### DEMO
![image.mov.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87900/03195928-1a79-8b47-5c51-0680f5652477.gif)

こうすることでEntityStoreはフロントエンドでのキャッシュにもなるし、データ構造自体も理解しやすくなるかもしれない。。。と思った。でも、もう少しStoreとEntityStoreは疎結合にすべきな気もする :thinking: 他にもっといい方法があったら誰か教えてください。。。 :sweat_smile: 
[Github](https://github.com/ichiwa/vue-akita-paging)