# ER図

## 概要

Decap CMSはGitリポジトリをプライマリストレージとして使用するヘッドレスCMSであり、従来のRDBMSは使用していない。以下のER図は、アプリケーション内のデータモデル間のリレーションシップを論理的に表現したものである。

## 全体ER図

```mermaid
erDiagram
    CmsConfig ||--o{ CmsCollection : "collections"
    CmsConfig ||--|| CmsBackend : "backend"
    CmsConfig ||--o| CmsMediaLibrary : "media_library"
    CmsConfig ||--o| CmsSlug : "slug"
    CmsConfig ||--o| CmsI18nConfig : "i18n"

    CmsCollection ||--o{ CmsField : "fields"
    CmsCollection ||--o{ CmsCollectionFile : "files (file_based)"
    CmsCollection ||--o{ ViewFilter : "view_filters"
    CmsCollection ||--o{ ViewGroup : "view_groups"
    CmsCollection ||--o{ SortableField : "sortable_fields"

    CmsCollectionFile ||--o{ CmsField : "fields"

    CmsField ||--o{ CmsField : "fields (nested)"
    CmsField ||--o| CmsField : "field (list item)"
    CmsField ||--o{ CmsField : "types (variable type)"

    CmsCollection ||--o{ EntryValue : "entries"
    EntryValue ||--o{ MediaFile : "mediaFiles"
    EntryValue ||--o| EntryI18n : "i18n"

    CmsCollection ||--o{ UnpublishedEntry : "unpublished_entries"
    UnpublishedEntry ||--o{ UnpublishedEntryDiff : "diffs"

    CmsConfig {
        string backend "バックエンド設定"
        string[] collections "コレクション定義"
        string media_folder "メディアフォルダパス"
        string public_folder "公開フォルダパス"
        string publish_mode "公開モード"
        string locale "ロケール"
        string site_url "サイトURL"
    }

    CmsBackend {
        string name "バックエンドタイプ"
        string repo "リポジトリ"
        string branch "ブランチ"
        string api_root "APIルート"
        boolean open_authoring "Open Authoring"
        boolean squash_merges "スカッシュマージ"
    }

    CmsCollection {
        string name "コレクション名(PK)"
        string label "表示ラベル"
        string type "タイプ(folder/files)"
        string folder "フォルダパス"
        string format "ファイルフォーマット"
        string extension "拡張子"
        string slug "スラグテンプレート"
        boolean create "新規作成許可"
        boolean delete "削除許可"
    }

    CmsCollectionFile {
        string name "ファイル名(PK)"
        string label "表示ラベル"
        string file "ファイルパス"
    }

    CmsField {
        string name "フィールド名"
        string label "表示ラベル"
        string widget "ウィジェットタイプ"
        boolean required "必須フラグ"
        string default "デフォルト値"
        string media_folder "メディアフォルダ"
    }

    EntryValue {
        string collection "コレクション名(FK)"
        string slug "スラグ(PK)"
        string path "ファイルパス"
        string raw "生コンテンツ"
        object data "パース済みデータ"
        string author "作成者"
        string updatedOn "更新日時"
        string status "公開ステータス"
        boolean newRecord "新規フラグ"
    }

    EntryI18n {
        string locale "ロケール"
        object data "ロケール別データ"
    }

    MediaFile {
        string id "ファイルID(PK)"
        string name "ファイル名"
        string path "ファイルパス"
        number size "ファイルサイズ"
        string url "URL"
        boolean draft "ドラフトフラグ"
    }

    UnpublishedEntry {
        string collection "コレクション名(FK)"
        string slug "スラグ(PK)"
        string status "ワークフローステータス"
        string updatedAt "更新日時"
        string pullRequestAuthor "PR作成者"
    }

    UnpublishedEntryDiff {
        string id "差分ID"
        string path "ファイルパス"
        boolean newFile "新規ファイルフラグ"
    }

    ViewFilter {
        string id "フィルタID"
        string label "表示ラベル"
        string field "対象フィールド"
        string pattern "パターン"
    }

    ViewGroup {
        string id "グループID"
        string label "表示ラベル"
        string field "対象フィールド"
        string pattern "パターン"
    }

    SortableField {
        string field "フィールド名"
        string label "表示ラベル"
        string default_sort "デフォルトソート方向"
    }

    CmsMediaLibrary {
        string name "ライブラリ名"
        object config "設定"
    }

    CmsSlug {
        string encoding "エンコーディング"
        boolean clean_accents "アクセント除去"
        string sanitize_replacement "サニタイズ置換文字"
    }

    CmsI18nConfig {
        string structure "構造タイプ"
        string[] locales "ロケール一覧"
        string default_locale "デフォルトロケール"
    }
```

## Redux Store 状態管理ER図

```mermaid
erDiagram
    State ||--|| Auth : "auth"
    State ||--|| CmsConfigState : "config"
    State ||--|| Collections : "collections"
    State ||--|| Entries : "entries"
    State ||--|| EntryDraft : "entryDraft"
    State ||--|| EditorialWorkflow : "editorialWorkflow"
    State ||--|| MediaLibraryState : "mediaLibrary"
    State ||--|| Medias : "medias"
    State ||--|| Search : "search"
    State ||--|| Integrations : "integrations"
    State ||--|| Cursors : "cursors"
    State ||--|| Deploys : "deploys"
    State ||--|| GlobalUI : "globalUI"
    State ||--|| StatusState : "status"
    State ||--|| NotificationsState : "notifications"

    Entries ||--o{ EntryMap : "entities"
    Entries ||--o{ PagesInfo : "pages"
    Entries ||--o| Sort : "sort"
    Entries ||--o| Filter : "filter"
    Entries ||--o| Group : "group"

    EditorialWorkflow ||--o{ EntryMap : "entities"
    EditorialWorkflow ||--o{ PagesInfo : "pages"

    EntryDraft ||--o| EntryMap : "entry"
    EntryDraft ||--o| FieldsMetaData : "fieldsMetaData"
    EntryDraft ||--o| FieldsErrors : "fieldsErrors"

    MediaLibraryState ||--o{ MediaFileItem : "files"
    MediaLibraryState ||--o{ DisplayURLState : "displayURLs"

    Search ||--o{ EntryId : "entryIds"
    Search ||--o{ QueryHits : "queryHits"

    State {
        string description "Redux Store ルート状態"
    }

    Auth {
        boolean isFetching "認証処理中"
        object user "ユーザー情報"
        string error "エラー"
    }

    CmsConfigState {
        object backend "バックエンド設定"
        object[] collections "コレクション定義"
        boolean isFetching "取得中"
        string error "エラー"
    }

    Collections {
        string description "OrderedMap: コレクション名 => Collection"
    }

    Entries {
        object entities "Map: collection.slug => EntryMap"
        object pages "Map: collection => PagesInfo"
        object sort "ソート設定"
        string viewStyle "表示スタイル"
    }

    EntryDraft {
        object entry "編集中エントリ"
        object fieldsMetaData "フィールドメタデータ"
        object fieldsErrors "バリデーションエラー"
        boolean hasChanged "変更フラグ"
        string key "UUID"
    }

    EditorialWorkflow {
        object entities "未公開エントリ"
        object pages "ページング情報"
    }

    MediaLibraryState {
        boolean isVisible "表示状態"
        object[] files "ファイル一覧"
        boolean isLoading "読込中"
        boolean isPersisting "保存中"
    }

    Medias {
        string description "path => AssetProxy, isLoading, error"
    }

    Search {
        boolean isFetching "検索中"
        string term "検索語"
        number page "ページ"
        object[] entryIds "結果ID一覧"
    }

    Integrations {
        object providers "プロバイダ設定"
        object hooks "フック設定"
    }

    Cursors {
        object cursorsByType "カーソル情報"
    }

    Deploys {
        string description "collection.slug => isFetching, url, status"
    }

    GlobalUI {
        boolean isFetching "ローディング"
        boolean useOpenAuthoring "Open Authoring"
    }

    StatusState {
        boolean isFetching "取得中"
        object status "API-認証ステータス"
    }

    NotificationsState {
        object[] notifications "通知一覧"
    }

    EntryMap {
        string collection "コレクション名"
        string slug "スラグ"
        string path "パス"
        object data "データ"
        string status "ステータス"
    }

    PagesInfo {
        boolean isFetching "取得中"
        number page "ページ番号"
        string[] ids "スラグ一覧"
    }

    Sort {
        string description "collection => OrderedMap: key => direction"
    }

    Filter {
        string description "collection => field => active"
    }

    Group {
        string description "collection => field => active"
    }

    FieldsMetaData {
        string description "フィールドメタデータMap"
    }

    FieldsErrors {
        string description "フィールドエラーMap"
    }

    MediaFileItem {
        string name "ファイル名"
        string id "ID"
        string path "パス"
        string key "UIキー"
    }

    DisplayURLState {
        boolean isFetching "取得中"
        string url "表示URL"
        object err "エラー"
    }

    EntryId {
        string collection "コレクション名"
        string slug "スラグ"
    }

    QueryHits {
        string namespace "名前空間"
        object[] hits "ヒット結果"
    }
```

## データストア層別データフロー図

```mermaid
flowchart TB
    subgraph Browser["ブラウザ"]
        subgraph ReduxStore["Redux Store (メモリ)"]
            auth["auth"]
            config["config"]
            collections["collections"]
            entries["entries"]
            entryDraft["entryDraft"]
            editorialWorkflow["editorialWorkflow"]
            mediaLibrary["mediaLibrary"]
            medias["medias"]
            search["search"]
        end

        subgraph LocalForage["localForage (IndexedDB)"]
            fileCache["File Content Cache"]
            metaCache["File Metadata Cache"]
            treeCache["Local Tree Cache"]
            backup["Entry Backup"]
        end

        subgraph LocalStorage["localStorage"]
            sortConfig["Sort Config"]
            viewStyle["View Style"]
            authStore["Auth Store"]
            editorUI["Editor UI Settings"]
        end
    end

    subgraph GitBackend["Git Backend"]
        github["GitHub API"]
        gitlab["GitLab API"]
        bitbucket["Bitbucket API"]
        gitea["Gitea API"]
        azure["Azure DevOps API"]
        proxy["Local Proxy Server"]
    end

    subgraph GitRepo["Git Repository"]
        contentFiles["Content Files (entries)"]
        mediaFiles["Media Files"]
        branches["Branches / PRs"]
    end

    entries <-->|"読み込み/書き込み"| GitBackend
    editorialWorkflow <-->|"読み込み/書き込み"| GitBackend
    mediaLibrary <-->|"読み込み/書き込み"| GitBackend
    GitBackend <-->|"Git API"| GitRepo

    entries -->|"ソート設定保存"| sortConfig
    entries -->|"表示スタイル保存"| viewStyle
    auth -->|"ユーザー情報保存"| authStore
    entryDraft -->|"バックアップ保存"| backup

    GitBackend -->|"コンテンツキャッシュ"| fileCache
    GitBackend -->|"メタデータキャッシュ"| metaCache
    GitBackend -->|"ツリーキャッシュ"| treeCache
```
