Obsidianとマインドマップにインスパイアされた要約アプリの試作です。
- フレームワーク:React Router (v7系のFramework mode)
- バックエンド:Supabase
- ORM:drizzle
- データベース:PostgreSQL
- UIコンポーネント:Mantine
- スタイリング:CSS Modules
- 状態管理(client state):jotai
- 状態管理(server state):TanStack Query
- テスト:Vitest
npx supabase startnpx supabase stop次のコマンドでDBがリセットされ、supabase/seed.sqlによってシードデータが投入されます。
なお、このコマンドはコンテナを起動した状態で実行する必要があります。
npx supabase db reset次のコマンドで、アプリ操作やGUIでのDB操作によって更新されたデータをsupabase/seed.sqlにバックアップし、次回のシーディングに使うことができます。
npx supabase db dump -f supabase/seed.sql --data-only --local作成されたsupabase/seed.sqlを開き、setvalの引数を書き換える必要があります。
直接INSERTした場合はシーケンスの最大値カウンタが更新されないので、次回のデータ追加時に備えて、シーケンスの最大値を適切に設定する必要があるからです。(参考:PostgreSQLの重複キー問題とシーケンスの調整方法)
たとえば、termsテーブルのidシーケンスの設定は次のようになっていますが、
SELECT pg_catalog.setval('"public"."terms_id_seq"', 1, true);これを次のように書き換えます。
SELECT pg_catalog.setval('"public"."terms_id_seq"', (select max(id) from "public"."terms"), true);app/db/schema.tsに登録されている他のテーブルについても、同様にsetvalの引数を書き換えてください。
app/db/schema.tsに変更を加えた場合は、次の手順でDBに反映します。
npx drizzle-kit generate次のコマンドはコンテナを起動した状態で実行する必要があります。
npx supabase db resetDependency cruiserを利用した、ファイルやディレクトリの依存関係チェックを導入しています。
npm run deps-checkコマンドで確認することができます。
npm run deps-graphコマンドで、コードの依存関係を可視化したグラフをdocs/dependency-graph.svgとして生成することができます。
このコマンドの実行にはGraphvizが必要となるため、次のようにマシンにインストールしてください。
brew install graphvizソースコードを配置するappディレクトリは、次のような構成になっています。
ページを表すコンポーネントです。
app/routes.tsに登録し、React RouterのRoute Moduleとして機能します。
- 初期データロードは
loader内で、app/queries/**/reader.tsを介して行います。 actionはここでは定義しない方針です。
APIエンドポイントを表すファイルです。
app/routes.tsに登録し、React RouterのResource Routesとして機能します。
- 各ファイルでは
actionかloaderを1つだけexportします。(GETの場合はloader、それ以外はaction) - データの取得は
app/queries/**/reader.tsを介して行います。 - データの変更は
app/usecases/**/feature.tsを介して行います。
UIコンポーネントを置く場所です。
- 見た目上の部品単位でディレクトリを分けます。
- そのコンポーネント専用のスタイル(CSS Module)やロジックも合わせて置くことができます。
- ただし、UIの状態は
app/(usecases|units)/**/ui.hooks.tsから参照するようにします。
ページ全体に適用したいグローバルCSSを置く場所です。
特定のライブラリや実行環境に依存するカスタム処理・型定義などはここにまとめます。
データベースのスキーマ定義や接続設定はここで行います。
Warning
ここで公開しているdbインスタンスには、app/units/**/repository.tsまたはapp/queries/**/readstore.tsのみがアクセスするようにします。
1つのオブジェクトで完結するロジックを扱う層です。
- アプリ上で意味を持つ概念(オブジェクト)ごとにディレクトリを分けます。
Warning
units配下のディレクトリ同士が依存しないようにします。
drizzle APIやSQLをラップした関数をここで定義し、データベース操作を隠蔽します。
Warning
service.tsのみがrepository.tsを呼び出すようにします。
reposiory.tsが提供する関数をラップし、アプリ上の意味付けを行います。
複数のオブジェクトに依存し、データの書き換えを伴う機能(コマンド)を実現する層です。
- アプリの機能ごとにディレクトリを分けます。
Warning
usecases配下のディレクトリ同士が依存しないようにします。
複数のunits/**/service.tsを組み合わせて、アプリの機能に即した形でロジックを実装します。
複数のオブジェクトに依存するデータの読み取り操作(クエリ)を組み立てる層です。
Warning
queries配下のディレクトリ同士が依存しないようにします。
drizzle APIやSQLをラップした関数を定義し、データベースからの読み取り操作とその最適化を隠蔽します。
Warning
reader.tsのみがreadstore.tsを呼び出すようにします。
readstore.tsやunits/**/service.tsが提供する読み取り専用の関数を組み合わせ、アプリ上で必要となる形でデータ取得処理をまとめます。
1つのオブジェクトで完結するUIの状態管理はunits配下で、複数のオブジェクトにまたがるUIの状態管理はusecases配下で行います。
UIの状態(jotaiのAtom)を定義します。
Warning
app/components配下のUIコンポーネントがここに直接アクセスしないようにします。
状態から算出される値(derived Atom)を定義します。
Warning
app/components配下のUIコンポーネントがここに直接アクセスしないようにします。
状態に変更を加える操作(write only Atom)を定義します。
Warning
app/components配下のUIコンポーネントがここに直接アクセスしないようにします。
UIの状態やその操作は、ここで定義したカスタムフックを介してコンポーネントに公開します。