LoginSignup
23
6

More than 3 years have passed since last update.

フロントエンドでDDDLikeなアーキテクチャを導入したときに困ったこと・対策(検討中も含む)

Posted at

前提のアーキテクチャ

スクリーンショット 2019-12-09 21.18.32.png

Presentation

表示を担う

Repository

外部との接続を担う。

Usecase(Application)

Repository層、Store層とのコネクション。
Repository層からとってきたデータをStoreに保存する。
ビジネスユースケースを記述。

Entity

モデル。(DDDに置けるドメインモデリング)

Query

Store層から取り出したデータを編集して取り出す。

開発言語,フレームワーク

Typescript, Angular

困ったこと・対策

Presenation編

Componentしていくが、再利用性がないものもComponent化していくのか迷った。

  • 「Google検索」、「I'm Feeling Lucky」と書かれているButtonは再利用性があるので、Component化しよう。
  • Googleのロゴはコンポーネント化するのか迷う スクリーンショット 2019-12-09 21.26.58.png

対策

一つの画面がもつComponentと再利用性があるComponentに分けた。

├── modules
    ├── GoogleLogoComponent
└── pages
    ├── GoogleLogoComponent

参考
https://tech.bitbank.cc/designsystem-theory-practice/

Entity編

Entity、値オブジェクトを記述するとき、巷で流行っているような記述はフロントエンドではコスパが悪い、、?


export class ValueObject {
  private _value: string;
  constructor(value: string) {
    this._value = value;
  }

  get getValue(): string {
    return this._value;
  }

  set setValue(value: string) {
    this._value = value;
  }
}

上記のように、一つのオブジェクトを表現するのにもこの記述がかかる。
そのくせ、基本的にフロントエンドでは一つのドメインオブジェクトにおけるドメインロジックが少ないことが多い(扱うもので異なる)

対策

記述をinterfaceにとどめた。Validationなどの記述は同じファイルにfunctionとして記述。

export interface ValueObject {
  value: string;
}

export function isExist(value: ValueObject): boolean {
  return this.value ? true : false;
}

値オブジェクトに関してはカプセルかする必要あるのか


UserIdのような値オブジェクトは、storeなどのライブラリと相性が悪い。
(userId: string | numberを想定してることが多い)

export interface UserId {
  value: string;
}

対策

uidのような値オブジェクトはライブラリとの相性の悪さを勘案して、typeでstringと同列な型とした。
これによって、引数を表す時などに、可視化はできるようになった。

export type UserId = string;

Query

query処理が煩雑になりがち。

ただapiからfetchしたJsonDataを使ってqueryを処理するのは難しくなりがち。

対策

正規化することで、RDBのようにquery処理を記述することができる。

参考
https://www.slideshare.net/ayatas0623/reduxstate-129830690

Store

storeの分割単位はPage単位?ドメインオブジェクト単位???

一つのpageでは、複数のドメインオブジェクトを扱うことが多い。したがって、ドメインオブジェクト単位でのStoreの設計だと、扱うStoreも多い。対してPage単位でStoreを構成することで、Storeの扱いがPageのなかの限定的になるが、扱いやすくなる。

対策

現状ドメインオブジェクト単位にしています。ただ、これといった明確な理由はないのでベストプラクティスを模索中です、。

参考
https://qiita.com/oi5u/items/4e2c5bc1d546be1b1c5d

Usecase

データ取得するのはどこの責務?

データ取得する場所は使っているフレームワーク、ライブラリなどで結構別れたりする。例えばVue.jsの場合はVuexをStoreで使ったりすることが多いが、Vuexのアクションでは非同期処理が勧められている。

参考
https://vuex.vuejs.org/ja/guide/actions.html

つまり、Presentationで発火し、ActionをDispatch。そこで非同期でAPIを発火させ、ミューテーションに値を送る形になっている。ただ、ライブラリで勧められているならまだしも、StoreでRepositoryを扱うのはどうかと疑問に思った。

対策

InitializeUsecaseとして、Usecase層で処理を行う。

export class InitializeUsecase {
  constructor() {}

  exec() {
   const repository = new EntityRepository()
   const entity = repository.fetch()
   const store = new Store()
   store.dispatch(entity)
  }
}

最後に

自分がフロントエンドDDDをする際に困ったところをかいつまんでまとめました。誰かの参考になりますと幸いです。

23
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
6