Upgrade to Pro — share decks privately, control downloads, hide ads and more …

フロントエンドの複雑さに立ち向かう / Tackling Complexity of Fron...

フロントエンドの複雑さに立ち向かう / Tackling Complexity of Front-end Software with DDD and Clean Architecture

フロントエンドの複雑さに立ち向かう
〜 DDD と Clean Architecture を携えて 〜

さくらのテックランチvol.6 〜ローストチキンのフロントエンドパスタとクリスマスFigmaケーキ〜
https://sakura-tokyo.connpass.com/event/303232/

YouTube配信アーカイブ
https://www.youtube.com/watch?v=usmLmI1bj74&t=472s

ドメイン駆動設計(Domain-Driven Design)や Clean Architecture をヨイショもディスもせずフラットな立場で評価し、現実解を探りながらフロントエンドの複雑さに立ち向かった半年間の軌跡

ダーシノ

December 19, 2023
Tweet

More Decks by ダーシノ

Other Decks in Programming

Transcript

  1. なぜフロントエンドは複雑になるのか? コア部分のトレンドがだいたい5年周期で替わる(※1) 秘伝のタレ化したコードは Smart UI(利口なUI)(※2) になりがち リッチなUIはビジネスロジックと同じくらい実装がいる (BFFを使わない場合) 複数のバックエンドの差異を吸収する NOTE:

    ※1: 超ざっくりフロントエンド年表 … jQuery(2006) → Backbone.js/AngularJS(2010) → → React(2013)/Vue.js(2014) → React16 関数コンポーネント(2018)/Vue3 CompositionAPI(2020) ※2: Smart UI(利口なUI)… UI(View層)にビジネスロジックが含まれている状態。DDD ではアンチパターン。 ただし、小さなアプリケーションの場合には開発効率の面で有効な場合もある。
  2. 注目した設計思想・哲学 ドメイン駆動設計 (Domain-Driven Design) エリック・エヴァンスのドメイン駆動設計 著: Eric Evans https://www.shoeisha.co.jp/book/detail/9784798121963 Clean

    Architecture Clean Architecture 達人に学ぶソフトウェアの構造と設計 著: Robert C.Martin https://www.kadokawa.co.jp/product/301806000678/
  3. ドメイン駆動設計 の 良いところ と イマイチなところ 良いところ 共通言語を作り、全員で共有する ビジネスとコードを対応付ける ビジネス視点で責務を分割できる コミュニケーションの重要性を

    説いている ドメインモデルを成長させる前提 イマイチなところ 書籍本文が難解でチーム全員で 共通認識を持つのが難しい 20年前のJava全盛期に書かれた ため内容が古い DDD用語と別文脈のキーワードが コンフリクトしていて誤解しやすい
  4. Clean Architecture の 良いところ と イマイチなところ 良いところ 関心・責務ごとにレイヤーをわける レイヤー間の依存方向を制限する 具体的な決定を遅延させる

    イマイチなところ 書籍を読まずに例の同心円図を 見ると誤解が発生しやすい フレームワークの制約があるため キレイにハマらない
  5. Domain層サンプルコード class Task extends Entity { #title: string ... isDone():

    boolean { ... } isExpired(): boolean { ... } /** * @deprecated * 本来ならEntityには変更系のメソッドも生えるのだが、 * JSフレームワークはメンバ変数の変更を検知できないので使えない */ changeTitle(newTitle: string) { ... } }
  6. Presentation層 UIを扱う、Presentation内はMVCアーキテクチャ View HTML(JSX)/CSS を書くところ ロジックは ViewModel に任せる ViewModel Vue.js

    の Computed/Methods、React の カスタムHooks UIに関するロジック(データ加工やUI制御など) ViewController Presentation層とApplication層を繋ぐ薄いレイヤー
  7. Presentation層サンプルコード const task = await controller.findByID(query.id) const { formattedTitle }

    = useTask(task) <UI> <h1>{ formattedTitle }</h1> { task.isDone() ? <button onClick={() => controller.delete(task)}>Delete</button> : <button onClick={() => controller.update(task, input)}>Update</button> } </UI> // ViewModel function useTask(task: Task) { const formattedTitle = useMemo(() => { const status = task.isExpired() ? 'Warning' : 'Progress' const title = task.title() return `${status}: ${title}` }, [...]) return { formattedTitle } }
  8. Application層サンプルコード class TaskInteractor implements ITaskUsecase { async findByID(id: TaskID) {

    const task = await repository.findByID(id) return task } async delete(task: Task) { await repository.delete(task) } async update(task: Task, input: FormInput) { // 本来なら書き換えた Task をそのまま Repository に渡せば良いのだが、 // メンバ変数の書き換えはJSフレームワークで検知できないため DTO で代替する const dto = new TaskDTO(task, input) const newTask = await repository.save(dto) return newTask } }
  9. Infrastructure層サンプルコード class TaskRepository implements ITaskRepository { #cache: Tasks[] async findByID(id:

    TaskID) { const isCached = this.#cache.some(a => a.id === id) if (isCached) { return this.#cache.find(a => a.id === id) } const res = await apiClient.get(`/task/${id}`, { ... }) const task = new Task(res) return task } async delete(task: Task) { const res = await apiClient.delete(`/task/${task.id}`) } async update(dto: TaskDTO) { const body = dto.createBody() const res = await apiClient.put(`/task/${dto.id}`, { body }) const newTask = new Task(res) return newTask } }
  10. 改善の余地あり 前提知識(DDDやClean Architecture、OOPなど)が増えたため プロジェクトの参入障壁が上がった 「前提知識を持っていたのでレガシーコードと比べて仕様が 理解しやすかった」 という感想もあった(11月入社の方) フレームワークの制約でアーキテクチャを厳密に適用できない Class-Based OOP

    と JSフレームワーク の相性が悪い データ指向プログラミングのほうが相性いいかも(未検証 冗長的なコードが増え、エディタ内で迷子になる コードジャンプしても思い通りの場所に飛ばない 似たような名前のファイルが増えて探しづらい