# Actual Budget コードリーディングガイドライン

## はじめに

このガイドラインは、Actual Budget（ローカルファースト個人財務管理システム）のコードベースを効率的に理解するための手引きです。
TypeScript/JavaScriptに精通していないエンジニアでも、段階的に学習できるよう構成されています。

**対象読者:**
- プロジェクトに新規参画するエンジニア
- 他言語からの経験者
- コードレビューを行う担当者

---

## 1. 言語基礎

> このセクションでは、TypeScript/JavaScriptの基本構文と概念を解説します。

### 1.1 プログラム構造

Actual Budgetは**TypeScript**を主要言語として使用し、一部レガシーコードにJavaScriptが残っています。TypeScriptはJavaScriptに静的型付けを追加した言語で、コンパイル時に型エラーを検出できます。

```typescript
// ファイル: packages/loot-core/src/types/models/transaction.ts:8-39
export type TransactionEntity = {
  id: string;
  is_parent?: boolean;
  is_child?: boolean;
  parent_id?: TransactionEntity['id'];
  account: AccountEntity['id'];
  category?: CategoryEntity['id'];
  amount: IntegerAmount;
  payee?: PayeeEntity['id'];
  notes?: string;
  date: string;
  // ... その他のプロパティ
};
```

**ポイント:**
- `export type` で型定義をエクスポート
- `?` はオプショナルプロパティ（省略可能）
- `TransactionEntity['id']` は他の型からプロパティの型を参照

### 1.2 データ型と変数

TypeScriptの基本的なデータ型を解説します。

```typescript
// ファイル: packages/loot-core/src/types/models/transaction.ts:1-6
import { type IntegerAmount } from '../../shared/util';

import { type AccountEntity } from './account';
import { type CategoryEntity } from './category';
import { type PayeeEntity } from './payee';
import { type ScheduleEntity } from './schedule';
```

**基本型:**
- `string`: 文字列
- `number`: 数値
- `boolean`: 真偽値
- `null`, `undefined`: 空値
- カスタム型: `IntegerAmount`, `TransactionEntity` など

**型インポート:**
- `import { type X }` は型のみをインポート（実行時コードには含まれない）

### 1.3 制御構造

```typescript
// ファイル: packages/loot-core/src/server/main.ts:97-117
handlers['set-server-url'] = async function ({ url, validate = true }) {
  if (url == null) {
    await asyncStorage.removeItem('user-token');
  } else {
    url = url.replace(/\/+$/, '');

    if (validate) {
      const result = await runHandler(handlers['subscribe-needs-bootstrap'], {
        url,
      });
      if ('error' in result) {
        return { error: result.error };
      }
    }
  }

  await asyncStorage.setItem('server-url', url);
  return {};
};
```

**ポイント:**
- `if/else` で条件分岐
- `async/await` で非同期処理を同期的に記述
- `in` 演算子でオブジェクトのプロパティ存在確認

### 1.4 関数/メソッド定義

```typescript
// ファイル: packages/loot-core/src/server/budget/app.ts:278-298
async function createCategory({
  name,
  groupId,
  isIncome,
  hidden,
}: {
  name: string;
  groupId: CategoryGroupEntity['id'];
  isIncome?: boolean;
  hidden?: boolean;
}): Promise<CategoryEntity['id']> {
  if (!groupId) {
    throw APIError('Creating a category: groupId is required');
  }

  return await db.insertCategory({
    name: name.trim(),
    cat_group: groupId,
    is_income: isIncome ? 1 : 0,
    hidden: hidden ? 1 : 0,
  });
}
```

**ポイント:**
- `async function` で非同期関数を定義
- 引数はオブジェクト分割代入でパラメータを受け取る
- 型注釈で引数と戻り値の型を明示
- `Promise<T>` は非同期処理の戻り値型

### 1.5 モジュール/インポート

```typescript
// ファイル: packages/loot-core/src/server/main.ts:1-44
import './polyfills';
import * as injectAPI from '@actual-app/api/injected';

import * as asyncStorage from '../platform/server/asyncStorage';
import * as connection from '../platform/server/connection';
import * as fs from '../platform/server/fs';
import { logger, setVerboseMode } from '../platform/server/log';
import * as sqlite from '../platform/server/sqlite';
import { q } from '../shared/query';
import { amountToInteger, integerToAmount } from '../shared/util';
import { type Handlers } from '../types/handlers';

import { app as accountsApp } from './accounts/app';
import { app as adminApp } from './admin/app';
```

**インポートパターン:**
- `import * as X from 'path'`: 名前空間としてすべてインポート
- `import { X, Y } from 'path'`: 名前付きエクスポートを選択的にインポート
- `import { X as Y } from 'path'`: 別名でインポート
- `import './file'`: 副作用のみインポート

**パスエイリアス:**
- `@actual-app/xxx`: ワークスペースパッケージへの参照
- `@desktop-client/xxx`: desktop-clientパッケージ内のパス

---

## 2. プロジェクト固有の概念

> このセクションでは、当プロジェクト特有の概念を解説します。

### 2.1 フレームワーク固有の概念

#### React（フロントエンド）

Actual Budgetは**React**をUIフレームワークとして使用します。

```tsx
// ファイル: packages/desktop-client/src/components/App.tsx:52-162
function AppInner() {
  const [budgetId] = useMetadataPref('id');
  const [cloudFileId] = useMetadataPref('cloudFileId');
  const { t } = useTranslation();
  const { showBoundary: showErrorBoundary } = useErrorBoundary();
  const dispatch = useDispatch();
  const userData = useSelector(state => state.user.data);

  useEffect(() => {
    setI18NextLanguage(null);
  }, []);

  // ... 中略 ...

  return budgetId ? <FinancesApp /> : <ManagementApp />;
}
```

**React概念:**
- **関数コンポーネント**: 関数として定義されるUIコンポーネント
- **フック (Hooks)**: `use` で始まる関数で状態管理やライフサイクルを制御
  - `useState`: コンポーネントの状態管理
  - `useEffect`: 副作用の実行（API呼び出し、イベントリスナー設定など）
  - `useSelector`: Reduxストアから状態を取得
  - `useDispatch`: Reduxアクションをディスパッチ

#### Redux Toolkit（状態管理）

グローバルな状態管理に**Redux Toolkit**を使用します。

```typescript
// ファイル: packages/desktop-client/src/index.tsx:35-50
const boundActions = bindActionCreators(
  {
    ...accountsSlice.actions,
    ...appSlice.actions,
    ...budgetSlice.actions,
    ...budgetfilesSlice.actions,
    ...modalsSlice.actions,
    ...notificationsSlice.actions,
    ...payeesSlice.actions,
    ...prefsSlice.actions,
    ...transactionsSlice.actions,
    ...tagsSlice.actions,
    ...usersSlice.actions,
  },
  store.dispatch,
);
```

**Slice構造:**
- 各機能ドメインごとに`xxxSlice.ts`ファイルを作成
- `actions`はReducerアクションを定義
- `store.dispatch`でアクションを発行

#### Express（バックエンド）

同期サーバーは**Express**フレームワークを使用します。

```typescript
// ファイル: packages/sync-server/src/app.ts:20-68
const app = express();

app.use(cors());
app.use(express.json({ limit: `${config.get('upload.fileSizeLimitMB')}mb` }));

app.use('/sync', syncApp.handlers);
app.use('/account', accountApp.handlers);
app.use('/gocardless', goCardlessApp.handlers);
app.use('/simplefin', simpleFinApp.handlers);
```

**Express概念:**
- **ミドルウェア**: `app.use()` でリクエスト処理チェーンを構成
- **ルーティング**: URLパスに対してハンドラーをマッピング
- **ハンドラー**: `(req, res) => {}` 形式でリクエストを処理

#### Electron（デスクトップアプリ）

デスクトップ版は**Electron**で構築されています。

```typescript
// ファイル: packages/desktop-electron/index.ts:429-497
app.on('ready', async () => {
  const globalPrefs = await loadGlobalPrefs();

  if (globalPrefs.syncServerConfig?.autoStart) {
    await startSyncServer();
  }

  protocol.handle('app', request => {
    // プロトコルハンドリング
  });

  await createWindow();
  await createBackgroundProcess();
});
```

**Electron概念:**
- **メインプロセス**: Node.js環境で動作、システムリソースにアクセス
- **レンダラープロセス**: Chromium環境でUIを描画
- **IPC**: プロセス間通信でデータをやり取り

### 2.2 プロジェクト独自のパターン

#### App/Handlers パターン

サーバーサイドのビジネスロジックは「App」パターンで構成されています。

```typescript
// ファイル: packages/loot-core/src/server/budget/app.ts:24-68
export type BudgetHandlers = {
  'budget/budget-amount': typeof actions.setBudget;
  'budget/copy-previous-month': typeof actions.copyPreviousMonth;
  // ... 他のハンドラー定義
};

export const app = createApp<BudgetHandlers>();

app.method('budget/budget-amount', mutator(undoable(actions.setBudget)));
app.method('budget/copy-previous-month', mutator(undoable(actions.copyPreviousMonth)));
```

**パターンの特徴:**
- 各機能ドメインごとに`app.ts`ファイルを作成
- `createApp<T>()`でアプリケーションインスタンスを生成
- `app.method(name, handler)`でAPIエンドポイントを登録
- `mutator()`でデータ変更操作をラップ
- `undoable()`でUndo機能を追加

#### カスタムフック パターン

Reactコンポーネントでの共通ロジックはカスタムフックとして抽出されています。

```typescript
// ファイル: packages/desktop-client/src/hooks/useAccounts.ts:1-20
import { useEffect } from 'react';
import { useInitialMount } from './useInitialMount';
import { getAccounts } from '@desktop-client/accounts/accountsSlice';
import { useDispatch, useSelector } from '@desktop-client/redux';

export function useAccounts() {
  const dispatch = useDispatch();
  const isInitialMount = useInitialMount();
  const isAccountsDirty = useSelector(state => state.account.isAccountsDirty);

  useEffect(() => {
    if (isInitialMount || isAccountsDirty) {
      dispatch(getAccounts());
    }
  }, [dispatch, isInitialMount, isAccountsDirty]);

  return useSelector(state => state.account.accounts);
}
```

**フック命名規則:**
- `use` プレフィックス必須
- データ取得: `useAccounts`, `useCategories`, `usePayees`
- 状態管理: `useMetadataPref`, `useGlobalPref`
- ユーティリティ: `useInitialMount`, `usePrevious`

#### Entity モデル パターン

データモデルはTypeScriptの型定義として管理されています。

```typescript
// ファイル: packages/loot-core/src/types/models/transaction.ts
export type TransactionEntity = {
  id: string;
  is_parent?: boolean;
  is_child?: boolean;
  parent_id?: TransactionEntity['id'];
  account: AccountEntity['id'];
  category?: CategoryEntity['id'];
  amount: IntegerAmount;
  // ...
};
```

**命名規則:**
- `XxxEntity`: データベースエンティティの型
- フィールドはスネークケース（`is_parent`, `cat_group`）

---

## 3. 命名規則

> このセクションでは、プロジェクト全体で使用される命名規則を解説します。

### 3.1 ファイル・ディレクトリ命名

| パターン | 意味 | 例 |
|---------|------|-----|
| `app.ts` | ドメインのAPIハンドラー定義 | `budget/app.ts`, `accounts/app.ts` |
| `xxxSlice.ts` | Redux Toolkitのスライス | `accountsSlice.ts`, `budgetSlice.ts` |
| `useXxx.ts` | カスタムフック | `useAccounts.ts`, `useCategories.ts` |
| `Xxx.tsx` | Reactコンポーネント | `App.tsx`, `Sidebar.tsx` |
| `xxx.test.ts` | テストファイル | `sync.test.ts`, `merge.test.ts` |
| `index.ts` | モジュールのエントリーポイント | `models/index.ts` |
| `xxx-types.ts` | 型定義専用ファイル | `ynab5-types.ts` |

### 3.2 クラス・関数・変数命名

| プレフィックス/サフィックス | 意味 | 例 |
|---------------------------|------|-----|
| `use` | Reactフック | `useAccounts`, `useDispatch` |
| `get` | データ取得関数 | `getAccounts`, `getCategories` |
| `set` | データ設定関数 | `setServer`, `setBudget` |
| `create` | エンティティ作成 | `createCategory`, `createWindow` |
| `delete` | エンティティ削除 | `deleteCategory`, `deleteCategoryGroup` |
| `update` | エンティティ更新 | `updateCategory`, `updateCategoryGroup` |
| `Entity` | 型名サフィックス | `TransactionEntity`, `AccountEntity` |
| `Handlers` | ハンドラー型 | `BudgetHandlers`, `Handlers` |
| `App` | アプリケーションコンポーネント | `FinancesApp`, `ManagementApp` |
| `Provider` | Contextプロバイダー | `ServerProvider`, `AuthProvider` |
| `is` | 真偽値プロパティ | `is_parent`, `is_child`, `isIncome` |
| `_` | 内部/一時プロパティ | `_unmatched`, `_deleted` |

### 3.3 プログラム分類一覧

| 分類 | 説明 | 配置場所 |
|------|------|---------|
| コアロジック | 予算管理、トランザクション処理 | `packages/loot-core/src/server/` |
| 型定義 | Entity、ハンドラーの型 | `packages/loot-core/src/types/` |
| プラットフォーム抽象化 | SQLite、ファイルシステム | `packages/loot-core/src/platform/` |
| UIコンポーネント | React画面部品 | `packages/desktop-client/src/components/` |
| カスタムフック | 再利用可能なロジック | `packages/desktop-client/src/hooks/` |
| Redux状態管理 | スライス、アクション | `packages/desktop-client/src/*/xxxSlice.ts` |
| 同期サーバー | Express APIサーバー | `packages/sync-server/src/` |
| Electronメイン | デスクトップアプリメイン | `packages/desktop-electron/` |

---

## 4. ディレクトリ構造

> このセクションでは、プロジェクトのディレクトリ構造を解説します。

```
actual-master/
├── packages/                       # モノレポのワークスペース
│   ├── loot-core/                  # コアビジネスロジック
│   │   ├── src/
│   │   │   ├── client/             # クライアント側ロジック
│   │   │   ├── mocks/              # テスト用モック
│   │   │   ├── platform/           # プラットフォーム抽象化層
│   │   │   │   ├── client/         # クライアントプラットフォーム
│   │   │   │   └── server/         # サーバープラットフォーム
│   │   │   ├── server/             # サーバーサイドロジック
│   │   │   │   ├── accounts/       # 口座管理
│   │   │   │   ├── budget/         # 予算管理
│   │   │   │   ├── schedules/      # スケジュール管理
│   │   │   │   ├── transactions/   # トランザクション管理
│   │   │   │   ├── rules/          # ルールエンジン
│   │   │   │   ├── sync/           # 同期処理
│   │   │   │   └── db/             # データベース操作
│   │   │   ├── shared/             # クライアント/サーバー共有
│   │   │   └── types/              # 型定義
│   │   │       └── models/         # エンティティ型
│   │   └── migrations/             # DBマイグレーション
│   │
│   ├── desktop-client/             # Webフロントエンド
│   │   ├── src/
│   │   │   ├── components/         # Reactコンポーネント
│   │   │   │   ├── accounts/       # 口座関連コンポーネント
│   │   │   │   ├── budget/         # 予算関連コンポーネント
│   │   │   │   ├── modals/         # モーダルダイアログ
│   │   │   │   ├── settings/       # 設定画面
│   │   │   │   └── sidebar/        # サイドバー
│   │   │   ├── hooks/              # カスタムフック
│   │   │   ├── accounts/           # 口座スライス
│   │   │   ├── budget/             # 予算スライス
│   │   │   ├── auth/               # 認証
│   │   │   └── style/              # スタイル定義
│   │   └── e2e/                    # E2Eテスト
│   │
│   ├── desktop-electron/           # Electronデスクトップアプリ
│   │   ├── index.ts                # メインプロセス
│   │   ├── preload.ts              # プリロードスクリプト
│   │   ├── menu.ts                 # メニュー定義
│   │   └── server.ts               # バックグラウンドサーバー
│   │
│   ├── sync-server/                # 同期サーバー
│   │   ├── src/
│   │   │   ├── app.ts              # Expressアプリ
│   │   │   ├── app-sync.ts         # 同期API
│   │   │   ├── app-account.ts      # アカウントAPI
│   │   │   ├── app-gocardless/     # GoCardless連携
│   │   │   └── app-simplefin/      # SimpleFin連携
│   │   └── migrations/             # サーバーDBマイグレーション
│   │
│   ├── api/                        # 公開API
│   ├── crdt/                       # CRDT実装
│   ├── component-library/          # 共通UIライブラリ
│   └── docs/                       # ドキュメント
│
├── bin/                            # ビルドスクリプト
└── docs/                           # プロジェクトドキュメント
```

### 各ディレクトリの役割

| ディレクトリ | 役割 | 主要ファイル |
|-------------|------|-------------|
| `packages/loot-core` | コアビジネスロジック（プラットフォーム非依存） | `main.ts`, `db/index.ts` |
| `packages/desktop-client` | React Webフロントエンド | `App.tsx`, `index.tsx` |
| `packages/desktop-electron` | Electronメインプロセス | `index.ts`, `preload.ts` |
| `packages/sync-server` | Express同期サーバー | `app.ts`, `app-sync.ts` |
| `packages/api` | 公開Node.js API | `index.ts` |
| `packages/crdt` | 同期用CRDT実装 | `timestamp.ts`, `merkle.ts` |

---

## 5. アーキテクチャ

> このセクションでは、プロジェクトのアーキテクチャパターンを解説します。

### 5.1 全体アーキテクチャ

Actual Budgetは**ローカルファースト**アーキテクチャを採用しています。

```mermaid
graph TB
    subgraph "Desktop App (Electron)"
        E[Electron Main Process]
        R[Renderer Process<br/>React UI]
        B[Background Server<br/>loot-core]
    end

    subgraph "Web Browser"
        W[React Web UI]
        WW[Web Worker<br/>loot-core]
    end

    subgraph "Sync Server (Optional)"
        S[Express Server]
        DB[(Server DB)]
    end

    subgraph "Local Storage"
        SQLite[(SQLite DB)]
        IDB[(IndexedDB)]
    end

    E --> R
    E --> B
    R <--> B
    B <--> SQLite

    W <--> WW
    WW <--> IDB

    B <-.->|Sync| S
    WW <-.->|Sync| S
    S <--> DB
```

**アーキテクチャの特徴:**
- **ローカルファースト**: データは常にローカルに保存され、同期はオプション
- **マルチプラットフォーム**: Electron（デスクトップ）、Web、API（Node.js）で動作
- **CRDT同期**: 競合解決にCRDTを使用し、オフライン対応

### 5.2 レイヤー構成

| レイヤー | 責務 | 代表的なファイル |
|---------|------|-----------------|
| プレゼンテーション | UIレンダリング、ユーザーインタラクション | `components/App.tsx`, `components/budget/` |
| アプリケーション | UIとドメインの仲介、状態管理 | `*Slice.ts`, `hooks/useXxx.ts` |
| ドメイン | ビジネスロジック、エンティティ | `server/budget/app.ts`, `server/transactions/` |
| インフラストラクチャ | データ永続化、外部サービス連携 | `server/db/`, `platform/server/sqlite/` |

### 5.3 データフロー

```
ユーザー操作
    ↓
React Component (UI)
    ↓ dispatch(action)
Redux Store
    ↓ send(message)
IPC / fetch
    ↓
loot-core Server (handlers)
    ↓ db.xxx()
SQLite / IndexedDB
    ↓ (Sync if enabled)
Sync Server
```

**フロー詳細:**

1. **ユーザー操作**: コンポーネントでイベント発生
2. **Redux Dispatch**: `dispatch()`でアクションを発行
3. **Thunk/Async Action**: 非同期処理でサーバーに`send()`
4. **IPC/Fetch**: Electron IPCまたはWeb Workerメッセージ
5. **Handler実行**: `loot-core`のハンドラーで処理
6. **DB操作**: SQLite/IndexedDBにデータ永続化
7. **同期**: 有効な場合、同期サーバーと通信

---

## 6. 主要コンポーネント

> このセクションでは、主要なコンポーネントとその連携を解説します。

### 6.1 エントリーポイント

#### Webフロントエンド

```tsx
// ファイル: packages/desktop-client/src/index.tsx:86-96
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
  <Provider store={store}>
    <ServerProvider>
      <AuthProvider>
        <App />
      </AuthProvider>
    </ServerProvider>
  </Provider>,
);
```

**構成:**
- `Provider`: Redux storeを提供
- `ServerProvider`: サーバー接続状態を管理
- `AuthProvider`: 認証状態を管理
- `App`: メインアプリケーションコンポーネント

#### Electronメインプロセス

```typescript
// ファイル: packages/desktop-electron/index.ts:429-497
app.on('ready', async () => {
  // OpenID設定があれば初期化
  if (globalPrefs.syncServerConfig?.autoStart) {
    await startSyncServer();
  }

  // appプロトコルハンドラー登録
  protocol.handle('app', request => { /* ... */ });

  // ブラウザウィンドウ作成
  await createWindow();

  // バックグラウンドサーバー起動
  await createBackgroundProcess();
});
```

#### コアサーバー

```typescript
// ファイル: packages/loot-core/src/server/main.ts:127-153
handlers = installAPI(handlers) as Handlers;

injectAPI.override((name, args) => runHandler(app.handlers[name], args));

app.handlers = handlers;
app.combine(
  authApp,
  schedulesApp,
  budgetApp,
  dashboardApp,
  // ... 各ドメインのアプリを結合
);
```

### 6.2 ビジネスロジック

予算管理のビジネスロジック例:

```typescript
// ファイル: packages/loot-core/src/server/budget/app.ts:69-135
app.method('budget/budget-amount', mutator(undoable(actions.setBudget)));
app.method('budget/copy-previous-month', mutator(undoable(actions.copyPreviousMonth)));
app.method('budget/set-zero', mutator(undoable(actions.setZero)));
// ...

app.method('category-create', mutator(undoable(createCategory)));
app.method('category-update', mutator(undoable(updateCategory)));
app.method('category-move', mutator(undoable(moveCategory)));
app.method('category-delete', mutator(undoable(deleteCategory)));
```

**ラッパー関数:**
- `mutator()`: データ変更操作をトランザクション化
- `undoable()`: Undo/Redo機能を追加

### 6.3 データアクセス

```typescript
// ファイル: packages/loot-core/src/server/budget/app.ts:293-298
return await db.insertCategory({
  name: name.trim(),
  cat_group: groupId,
  is_income: isIncome ? 1 : 0,
  hidden: hidden ? 1 : 0,
});
```

**dbモジュール機能:**
- `db.first()`: 1件取得
- `db.all()`: 複数件取得
- `db.runQuery()`: クエリ実行
- `db.insertCategory()`: カテゴリ挿入
- `db.updateCategory()`: カテゴリ更新
- `db.deleteCategory()`: カテゴリ削除

### 6.4 ユーティリティ/共通機能

```typescript
// ファイル: packages/loot-core/src/shared/util.ts（参照元）
import { amountToInteger, integerToAmount } from '../shared/util';
```

**金額処理:**
- 内部では整数（セント単位）で管理
- `amountToInteger()`: 浮動小数点 -> 整数
- `integerToAmount()`: 整数 -> 浮動小数点

---

## 7. よく使われるパターン

> このセクションでは、コード内で頻出するパターンを解説します。

### パターン一覧

| パターン | 説明 | 出現頻度 | 代表的なファイル |
|---------|------|---------|-----------------|
| App/Handlers | ドメインAPIの定義 | 高 | `*/app.ts` |
| Custom Hooks | 再利用可能なロジック | 高 | `hooks/useXxx.ts` |
| Slice Pattern | Redux状態管理 | 高 | `*Slice.ts` |
| Entity Types | データモデル定義 | 高 | `types/models/` |
| mutator/undoable | トランザクション＋Undo | 中 | `server/*/app.ts` |
| Provider Pattern | Context提供 | 中 | `*Provider.tsx` |

### 各パターンの詳細

#### パターン1: App/Handlers パターン

**目的:** ドメインごとのAPIエンドポイントを整理し、型安全に呼び出し可能にする

**実装例:**
```typescript
// ファイル: packages/loot-core/src/server/budget/app.ts:24-67
export type BudgetHandlers = {
  'budget/budget-amount': typeof actions.setBudget;
  'budget/copy-previous-month': typeof actions.copyPreviousMonth;
  // ...
};

export const app = createApp<BudgetHandlers>();

app.method('budget/budget-amount', mutator(undoable(actions.setBudget)));
```

**解説:**
- 型定義で全ハンドラーの引数・戻り値を明示
- `createApp<T>()`で型付きアプリインスタンス生成
- `app.method()`で実装を登録

#### パターン2: Custom Hooks パターン

**目的:** コンポーネント間で再利用可能なロジックを抽出

**実装例:**
```typescript
// ファイル: packages/desktop-client/src/hooks/useAccounts.ts:8-20
export function useAccounts() {
  const dispatch = useDispatch();
  const isInitialMount = useInitialMount();
  const isAccountsDirty = useSelector(state => state.account.isAccountsDirty);

  useEffect(() => {
    if (isInitialMount || isAccountsDirty) {
      dispatch(getAccounts());
    }
  }, [dispatch, isInitialMount, isAccountsDirty]);

  return useSelector(state => state.account.accounts);
}
```

**解説:**
- `use`プレフィックスで命名
- 内部で他のフックを使用可能
- データ取得とキャッシュ無効化を自動化

#### パターン3: Slice Pattern

**目的:** Redux状態を機能単位で分割管理

**実装例:**
```typescript
// 使用例: packages/desktop-client/src/index.tsx:17-31
import * as accountsSlice from './accounts/accountsSlice';
import * as appSlice from './app/appSlice';
import * as budgetSlice from './budget/budgetSlice';

const boundActions = bindActionCreators(
  {
    ...accountsSlice.actions,
    ...appSlice.actions,
    ...budgetSlice.actions,
    // ...
  },
  store.dispatch,
);
```

**解説:**
- 各スライスは独自の状態とアクションを持つ
- `bindActionCreators`でディスパッチにバインド
- 複数スライスを組み合わせてグローバル状態を構成

---

## 8. 業務フロー追跡の実践例

> このセクションでは、実際の業務フローをコードで追跡する方法を解説します。

### 8.1 フロー追跡の基本手順

1. エントリーポイントを特定
2. 処理の流れを追跡（呼び出し関係を追う）
3. データの変換を確認
4. 最終的な出力を確認

### 8.2 フロー追跡の実例

#### 例1: カテゴリ作成フロー

**概要:** ユーザーがUIから新しいカテゴリを作成する処理の流れ

**処理フロー:**
```
UI操作 → Redux dispatch → send() → Handler → DB挿入 → 応答
```

**詳細な追跡:**

1. **UIコンポーネント**
   ユーザーがカテゴリ追加ボタンをクリック

2. **Redux Action発行**
   ```typescript
   // クライアント側でsend()を呼び出し
   const result = await send('category-create', {
     name: 'Food',
     groupId: 'group-uuid',
   });
   ```

3. **Handler実行** (`packages/loot-core/src/server/budget/app.ts:140`)
   ```typescript
   app.method('category-create', mutator(undoable(createCategory)));
   ```

4. **ビジネスロジック** (`packages/loot-core/src/server/budget/app.ts:278-298`)
   ```typescript
   async function createCategory({
     name,
     groupId,
     isIncome,
     hidden,
   }) {
     if (!groupId) {
       throw APIError('Creating a category: groupId is required');
     }

     return await db.insertCategory({
       name: name.trim(),
       cat_group: groupId,
       is_income: isIncome ? 1 : 0,
       hidden: hidden ? 1 : 0,
     });
   }
   ```

5. **DB操作**
   SQLite/IndexedDBにカテゴリレコードを挿入

6. **応答返却**
   新しいカテゴリのIDがクライアントに返される

### 8.3 フロー追跡チェックリスト

- [ ] エントリーポイントを特定したか
- [ ] 呼び出し関係を把握したか
- [ ] データの変換ポイントを確認したか
- [ ] エラーハンドリングを確認したか
- [ ] 最終的な出力を確認したか

---

## 9. 設計書の参照順序

> このセクションでは、プロジェクト理解のための設計書参照順序を案内します。

### 9.1 目的別ロードマップ

#### 全体像を把握したい場合
1. `README.md` - プロジェクト概要
2. `AGENTS.md` - 開発ガイドライン
3. `packages/` 配下のディレクトリ構造確認
4. `package.json` - 依存関係とスクリプト

#### 特定機能を理解したい場合
1. 該当パッケージの`package.json`
2. 該当ドメインの`app.ts`（ハンドラー定義）
3. 型定義ファイル（`types/models/`）
4. 関連コンポーネント（`components/`）

#### 改修作業を行う場合
1. 対象機能のテストファイル（`*.test.ts`）
2. ハンドラー定義（`*/app.ts`）
3. 型定義の確認
4. E2Eテスト（`e2e/`）

### 9.2 ドキュメント一覧

| ドキュメント | 概要 | 参照タイミング |
|-------------|------|---------------|
| `README.md` | プロジェクト概要、インストール方法 | 初回参照 |
| `AGENTS.md` | AI/開発者向けガイドライン | 開発前 |
| `CODE_REVIEW_GUIDELINES.md` | コードレビュー基準 | PR作成時 |
| `CONTRIBUTING.md` | コントリビューションガイド | 貢献時 |
| `packages/docs/` | Docusaurusドキュメント | 詳細仕様確認 |

---

## 10. トラブルシューティング

> このセクションでは、コードリーディング時によくある問題と解決法を解説します。

### よくある疑問と回答

#### Q: `@actual-app/xxx` や `@desktop-client/xxx` のパスはどこを指している？
A: TypeScriptのパスエイリアスです。
- `@actual-app/xxx`: `packages/xxx` ワークスペースパッケージ
- `@desktop-client/xxx`: `packages/desktop-client/src/xxx`
- 設定は `tsconfig.json` で確認できます

#### Q: `send('handler-name', args)` は何をしている？
A: クライアントからサーバー（loot-core）へのRPC呼び出しです。
- Electron: IPCで通信
- Web: Web WorkerとpostMessageで通信
- ハンドラーは `packages/loot-core/src/server/` 配下の `app.ts` で定義

#### Q: `mutator()` と `undoable()` の違いは？
A:
- `mutator()`: データ変更をトランザクションでラップ
- `undoable()`: Undo/Redoスタックに操作を記録
- 通常は両方を組み合わせて使用: `mutator(undoable(fn))`

#### Q: 型定義はどこにある？
A:
- エンティティ型: `packages/loot-core/src/types/models/`
- ハンドラー型: `packages/loot-core/src/types/handlers.ts`
- 各`app.ts`内の `XxxHandlers` 型定義

### デバッグのヒント

1. **console.log の代わりに logger を使用**
   ```typescript
   import { logger } from '../platform/server/log';
   logger.log('Debug message');
   ```

2. **グローバル変数でデバッグ**
   開発モードでは以下がグローバルに公開:
   ```typescript
   global.$query  // クエリ実行
   global.$q      // クエリビルダー
   global.$send   // ハンドラー呼び出し
   global.$db     // DBアクセス
   ```

3. **React DevTools**
   コンポーネントツリーとRedux状態を確認

4. **ネットワークタブ**
   同期サーバーとの通信をモニタリング

---

## 付録

### A. 用語集

| 用語 | 説明 |
|-----|------|
| ローカルファースト | データを常にローカルに保存し、同期はオプションのアーキテクチャ |
| CRDT | Conflict-free Replicated Data Type、競合なしで複製できるデータ構造 |
| Envelope Budgeting | 封筒予算法、収入をカテゴリに割り当てる予算管理手法 |
| Handler | サーバーサイドのAPI関数 |
| Slice | Redux Toolkitにおける状態管理の単位 |
| Entity | データベースのレコードに対応する型 |
| Mutator | データ変更操作をラップする関数 |

### B. ファイル一覧

| ファイル/ディレクトリ | 説明 | 主な内容 |
|---------------------|------|---------|
| `packages/loot-core/src/server/main.ts` | コアサーバーエントリーポイント | ハンドラー登録、初期化 |
| `packages/desktop-client/src/index.tsx` | Webフロントエントリー | React アプリマウント |
| `packages/desktop-electron/index.ts` | Electronメインプロセス | ウィンドウ管理、IPC |
| `packages/sync-server/src/app.ts` | 同期サーバー | Express API定義 |
| `packages/loot-core/src/types/models/` | エンティティ型定義 | Transaction, Account等 |

### C. 参考資料

- [TypeScript公式ドキュメント](https://www.typescriptlang.org/docs/)
- [React公式ドキュメント](https://react.dev/)
- [Redux Toolkit公式ドキュメント](https://redux-toolkit.js.org/)
- [Electron公式ドキュメント](https://www.electronjs.org/docs/)
- [Express公式ドキュメント](https://expressjs.com/)
- [Actual Budget コミュニティドキュメント](https://actualbudget.org/docs)
