vue + akita의 상태 디자인 패턴

13005 단어 AkitaVue.js디자인

Akita란?



Akita 은 Datorama라는 판매 포스에 인수된 마케팅 회사에 개발되고 있는 OSS.
Rxjs로 만든 Angular, React, Vue 등의 상태 관리 라이브러리.

유행의 스테이트 관리에는 flux 실장의 Redux나 vuex등 있는데, 솔직히 말해 등장 인물이 너무 많아 중복 지나간다. 그러니까 mobx일지도 모르지만, 그렇다면 더 이상 akita로 좋지 않을까라고 생각한다.

※덧붙여서 akita는 chrome extension의 redux-devtool로 상태를 쫓는다.
자세한 컨셉은 이쪽

스토어



Akita에는 2종류의 Store가 존재한다.

스토어



Store는 스토어의 상태를 관리하는 단일 Object 등을 넣는 곳. 기본적으로 컬렉션 등은 넣어서는 안된다.
제공되는 API는 setLoading,setError,destroy만.

EntityStore



EntityStore는 데이터 등을 넣으면 Collection으로 관리해 준다.
이곳은 특수로 setLoading,setError,destroy는 당연히 제공되고 있지만 컬렉션 관리도 해줍니다.

e.g.

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가 관리해줍니다.
{
  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 아키텍처 이미지

등장 인물은 이하의 3종류.

서비스



Store를 업데이트합니다.
Component는 Service의 IF를 의식할 뿐.

스토어



위에서 설명했습니다.

Query



Store에서 데이터를 가져옵니다. Observable에 데이터를 취득할 수 있다.

기본적으로 다음 흐름.
  • Component로부터 Service를 통해 Store를 갱신
  • Component에서 Query로 Store 가져 오기

  • normalizr+redux와 같은 상태 설계



    여기에서는 redux+normalizr와 같은 entity와 id를 나누어 저장해, 표시하는 id만을 다른 것으로 관리하는 User 일람 페이지를 만들어 보자.
    - Normalizr를 사용한 Redux 구현 패턴

    세세한 곳은 Github에 오르고 있기 때문에 소스 코드를 읽고 싶습니다.

    1. UserList 컴퍼넌트는 표시하는 정보를 관리하는 이러한 Store를 갖는다.
    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


    2. API의 결과가 되는 User 정보를 관리하는 EntityStore를 shared에 가지는.
    API로 정보를 취득할 때마다 여기에 저장한 후, 위의 Store도 갱신한다.
    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


    3.Component에서는 1의 Store를 watch하고, 화면에 표시하는 UserList를 결정해 갱신한다.

    그러나





    이렇게 함으로써 EntityStore는 프런트 엔드에서의 캐시도 되고, 데이터 구조 자체도 이해하기 쉬워질지도 모른다. . . 라고 생각했다. 하지만 좀 더 Store와 EntityStore는 느슨하게 결합해야 할 생각도 한다. . .
    Github

    좋은 웹페이지 즐겨찾기