Closed40

Todoリスト育成日記

hajimismhajimism

ディレクトリ構成

hajimismhajimism

App Routerで、こうなってる

.
├── app
├── common
│   ├── components
│   │   ├── functional
│   │   ├── layout
│   │   └── ui
│   ├── hooks
│   └── lib
│       └── date
├── model
│   ├── todo
│   │   ├── components
│   │   ├── hooks
│   │   ├── lib
│   │   └── query
│   └── user
│       ├── components
│       ├── hooks
│       ├── lib
│       └── query
├── public
└── styles
hajimismhajimism

ファイルまで書くとこう

.
├── app
│   ├── favicon.ico
│   ├── layout.tsx
│   ├── page.stories.tsx
│   ├── page.tsx
│   └── providers.tsx
├── common
│   ├── components
│   │   ├── functional
│   │   ├── layout
│   │   └── ui
│   ├── hooks
│   │   └── debounce.ts
│   └── lib
│       ├── cn.ts
│       ├── date
│       ├── error.ts
│       ├── fetcher.ts
│       ├── generateId.ts
│       ├── getInitial.ts
│       ├── guard.ts
│       ├── log.ts
│       ├── result.ts
│       └── schema.ts
├── model
│   ├── todo
│   │   ├── atom.ts
│   │   ├── components
│   │   ├── hooks
│   │   ├── index.ts
│   │   ├── lib
│   │   ├── mock.ts
│   │   ├── query
│   │   └── type.ts
│   └── user
│       ├── atom.ts
│       ├── components
│       ├── hooks
│       ├── index.ts
│       ├── lib
│       ├── mock.ts
│       ├── query
│       └── type.ts

hajimismhajimism

app/common/modelで分けてる

  • app: routing + page特有のモジュール
  • common: どこでも使う、他のプロジェクトにも使い回せる、low-context
  • model: アプリケーション特有のモデルに関心があるモジュール、high-context
hajimismhajimism

features/xxxは「ディレクトリの機能的凝集」という本質的な良さがある一方で、機能同士の結合度が高いとき(例: 閲覧-プレビュー-編集)に分割基準をどこに置くかが難しいという問題があるのでnot for meだった。featuresを単にmodelとするだけで、線引が明確になることに気がついた。

実際workしていると思う。

hajimismhajimism

前まではcomponents/以下だけmodelを置いてて、libとかでもcommonとmodel分けたいなあっていう悩みがあった(正確には、とりあえずapp/でコロケーションさせてたものがページをまたいで使われるようになったときにどこに置くか迷った)のだが、そういう悩みが一切なくなった。

todo/lib/とかはこんなかんじ
https://github.com/hajimism/todo-list/blob/main/model/todo/lib/createNew.ts
https://github.com/hajimism/todo-list/blob/main/model/todo/lib/guard.ts

hajimismhajimism

モジュールの命名とかも意識はしてて、例えばuseDeleteTodomodel/todo/hooks/delete.tsにおいてある。これをmodel/todo/hooks/useDeleteTodo.tsとかいうファイル名にしないほうがいい。todo/hooks/の時点でuse〇〇Todoが導けるので、〇〇のところをファイル名に書くだけでいい。

hajimismhajimism

コンポーネントの命名・ファイル名も同様で、なるべくコンパクトで納得感が高いようにしている。

import { TodoTableRow } from "@/model/todo/components/table/row";

model/xxx/以下のモジュールは必ず名前にxxxが入るようにする。commonや他のモデルのモジュールとと区別するため。

hajimismhajimism

エラーハンドリング

hajimismhajimism

State設計

hajimismhajimism

Server StateはTanstack、それ以外のGlobal StateはJotaiでちょこちょこ書いている感じ

hajimismhajimism

App RouterのキャッシュとTanstackのキャッシュが二重にあって色々うざいなあって思ってたけどまだちゃんと向き合ってない

hajimismhajimism

二重Stateっぽさは否めないものの、setStateは直感的だし勝手にOptimisticになるし結構気に入っている

hajimismhajimism

fetchingが絡むとき、T[]に対する操作はTanstackで状態管理して、Tに対する操作はatomFamilyで状態管理するのは、これからもちょくちょく使うかも。

hajimismhajimism

そういえば、NextのRequest dedupingがうまく効いてなくて、このコンポーネントを複数並べたときにコンポーネントの数だけ/usersへのリクエストが飛んでたことがあった。当時はuseQueryで書いてた。

https://github.com/hajimism/todo-list/blob/main/model/todo/components/table/cell/assignee.container.tsx
https://nextjs.org/docs/app/building-your-application/caching#request-memoization

atomWithQueryにしたことでデータフェッチがコンポーネントの外側で行われるようになったので、1回で済むようになった。

hajimismhajimism

いやまあ、Nextが期待通りにworkするのが一番なんだけれど。

hajimismhajimism

Suspense設計

hajimismhajimism

Tanstack Queryはsuspense modeのときにundefinedを消して欲しい

hajimismhajimism

大体イメージこんな感じになる

some-component
  ├── container.tsx
  ├── index.tsx
  └── skeleton.tsx
<Suspense fallback={<ComponentSkeleton />}>
  <ComponentContainer />
</Suspense>
hajimismhajimism

Containerをコンポーネントの名前に含めることで、どれを<Susupense>で囲うべきなのか明示する。これもESLint rule書けそう。

hajimismhajimism

データフェッチもmodel側で完結するので、app側でやることは本当にただの組み立てだけだなーって感じ。

hajimismhajimism

一方で、コンポーネントの使用箇所ごとにfetchのoptionsを指定し直したかったらそう簡単にはいかなさそう。

hajimismhajimism

containerもSkeletonもindexからexportしたいのはある
ので、こうでも良いかも。

some-component
  ├── container.tsx
  ├── index.tsx
  ├── skeleton.tsx
  └── view.tsx
hajimismhajimism

単純なTodoリストのつもりだけど、それでも作るのこんなにむずいんだなっていう感想を持った。そりゃソフトウェア開発むずいわ。

たぶん10hも手を動かしてないと思うけど、その間にも設計の改善のために右往左往リファクタした。思いがけずいい経験になったし、やっぱり設計に向き合うのは好き。

チーム開発するとなると、設計思想は全員で共有してこそはじめて威力を発揮するので、言語化・布教活動も頑張っていきたい。特にディレクトリ構成みたいな大枠でスムーズに合意したい。

hajimismhajimism

まだ全然触ってないとこあるし、例えばo-tel入れてみたりOAuth実装してみたりCI/CDちゃんと組んでみたりServer Actions使ってみたりと試せることはたくさんあるので、気が向いたら都度育てていきたい。

このスクラップは2023/09/19にクローズされました