[{"data":1,"prerenderedAt":760},["ShallowReactive",2],{"navigation":3,"doc-/architecture/data-model":217,"docs-all-paths":705},[4,19,39,63,87,111,127,195,211],{"title":5,"path":6,"stem":7,"children":8},"Welcome","/welcome","01.welcome",[9,11,15],{"title":5,"path":6,"stem":10},"01.welcome/index",{"title":12,"path":13,"stem":14},"Astar とは（5分）","/welcome/product-in-5-min","01.welcome/01.product-in-5-min",{"title":16,"path":17,"stem":18},"事業文脈","/welcome/business-context","01.welcome/02.business-context",{"title":20,"path":21,"stem":22,"children":23},"Getting Started","/getting-started","02.getting-started",[24,27,31,35],{"title":25,"path":21,"stem":26},"はじめかた","02.getting-started/index",{"title":28,"path":29,"stem":30},"リポジトリ取得と環境構築","/getting-started/repo-setup","02.getting-started/01.repo-setup",{"title":32,"path":33,"stem":34},"日々の開発ループ","/getting-started/dev-loop","02.getting-started/02.dev-loop",{"title":36,"path":37,"stem":38},"検証の 4 層","/getting-started/verification-tiers","02.getting-started/03.verification-tiers",{"title":40,"path":41,"stem":42,"children":43},"Architecture","/architecture","03.architecture",[44,47,51,55,59],{"title":45,"path":41,"stem":46},"アーキテクチャ","03.architecture/index",{"title":48,"path":49,"stem":50},"マルチテナンシーと RLS","/architecture/multi-tenancy-rls","03.architecture/01.multi-tenancy-rls",{"title":52,"path":53,"stem":54},"認可モデル","/architecture/authorization","03.architecture/02.authorization",{"title":56,"path":57,"stem":58},"AI サブシステム","/architecture/ai-subsystem","03.architecture/03.ai-subsystem",{"title":60,"path":61,"stem":62},"データモデル","/architecture/data-model","03.architecture/04.data-model",{"title":64,"path":65,"stem":66,"children":67},"Backend","/backend","04.backend",[68,71,75,79,83],{"title":69,"path":65,"stem":70},"バックエンド","04.backend/index",{"title":72,"path":73,"stem":74},"層構造","/backend/layering","04.backend/01.layering",{"title":76,"path":77,"stem":78},"規約","/backend/conventions","04.backend/02.conventions",{"title":80,"path":81,"stem":82},"テスト","/backend/testing","04.backend/03.testing",{"title":84,"path":85,"stem":86},"マイグレーション","/backend/migrations","04.backend/04.migrations",{"title":88,"path":89,"stem":90,"children":91},"Frontend","/frontend","05.frontend",[92,95,99,103,107],{"title":93,"path":89,"stem":94},"フロントエンド","05.frontend/index",{"title":96,"path":97,"stem":98},"モジュール内部構造","/frontend/module-anatomy","05.frontend/01.module-anatomy",{"title":100,"path":101,"stem":102},"API アクセスの一本道","/frontend/api-access-chain","05.frontend/02.api-access-chain",{"title":104,"path":105,"stem":106},"i18n","/frontend/i18n","05.frontend/03.i18n",{"title":108,"path":109,"stem":110},"デザインシステム","/frontend/design-system","05.frontend/04.design-system",{"title":112,"path":113,"stem":114,"children":115},"Go Agent And Cli","/go-agent-and-cli","06.go-agent-and-cli",[116,119,123],{"title":117,"path":113,"stem":118},"Go agent と CLI","06.go-agent-and-cli/index",{"title":120,"path":121,"stem":122},"Go agent","/go-agent-and-cli/go-agent","06.go-agent-and-cli/01.go-agent",{"title":124,"path":125,"stem":126},"CLI","/go-agent-and-cli/cli","06.go-agent-and-cli/02.cli",{"title":128,"path":129,"stem":130,"children":131},"Modules","/modules","07.modules",[132,135,139,143,147,151,155,159,163,167,171,175,179,183,187,191],{"title":133,"path":129,"stem":134},"モジュール","07.modules/index",{"title":136,"path":137,"stem":138},"テーブル","/modules/table","07.modules/01.table",{"title":140,"path":141,"stem":142},"ドキュメントツリー","/modules/document","07.modules/02.document",{"title":144,"path":145,"stem":146},"AIチャット","/modules/ai","07.modules/03.ai",{"title":148,"path":149,"stem":150},"認証・権限","/modules/auth","07.modules/04.auth",{"title":152,"path":153,"stem":154},"AI取込","/modules/organize","07.modules/05.organize",{"title":156,"path":157,"stem":158},"ストレージ","/modules/storage","07.modules/06.storage",{"title":160,"path":161,"stem":162},"NAS同期","/modules/sync","07.modules/07.sync",{"title":164,"path":165,"stem":166},"ワークスペース","/modules/workspace","07.modules/08.workspace",{"title":168,"path":169,"stem":170},"テナントとメンバーシップ","/modules/tenant-membership","07.modules/09.tenant-membership",{"title":172,"path":173,"stem":174},"検索","/modules/search","07.modules/10.search",{"title":176,"path":177,"stem":178},"表示ビュー","/modules/display","07.modules/11.display",{"title":180,"path":181,"stem":182},"テンプレート","/modules/template","07.modules/12.template",{"title":184,"path":185,"stem":186},"メール","/modules/mail","07.modules/13.mail",{"title":188,"path":189,"stem":190},"電話","/modules/phone","07.modules/14.phone",{"title":192,"path":193,"stem":194},"リアルタイム","/modules/realtime","07.modules/15.realtime",{"title":196,"path":197,"stem":198,"children":199},"Infra And Deploy","/infra-and-deploy","08.infra-and-deploy",[200,203,207],{"title":201,"path":197,"stem":202},"インフラとデプロイ","08.infra-and-deploy/index",{"title":204,"path":205,"stem":206},"環境の種類","/infra-and-deploy/environments","08.infra-and-deploy/01.environments",{"title":208,"path":209,"stem":210},"セルフホスト構成","/infra-and-deploy/self-host","08.infra-and-deploy/02.self-host",{"title":212,"path":213,"stem":214,"children":215},"docs-map","/docs-map","09.docs-map/index",[216],{"title":212,"path":213,"stem":214},{"id":218,"title":60,"body":219,"description":699,"extension":700,"meta":701,"navigation":702,"path":61,"seo":703,"stem":62,"__hash__":704},"docs/03.architecture/04.data-model.md",{"type":220,"value":221,"toc":690},"minimark",[222,225,238,243,246,296,306,312,335,345,358,377,385,394,433,462,466,473,600,618,640,644,655,670,673,686],[223,224,60],"h1",{"id":60},[226,227,228,229,233,234,237],"p",{},"Astar のデータモデルには、他の一般的な Spring Boot プロジェクトと違う前提が2つあります。",[230,231,232],"strong",{},"すべてのエンティティが二重の ID を持つこと","と、",[230,235,236],{},"ORM が Exposed（JPA/Hibernate ではない）であること","です。",[239,240,242],"h2",{"id":241},"dual-id-規約","Dual-ID 規約",[226,244,245],{},"すべての永続化エンティティは、内部用の連番 ID と外部公開用の UUID の両方を持ちます。",[247,248,249,265],"table",{},[250,251,252],"thead",{},[253,254,255,259,262],"tr",{},[256,257,258],"th",{},"ID",[256,260,261],{},"型",[256,263,264],{},"用途",[266,267,268,283],"tbody",{},[253,269,270,277,280],{},[271,272,273],"td",{},[274,275,276],"code",{},"id",[271,278,279],{},"BIGINT",[271,281,282],{},"内部 PK（自動生成、JOIN のパフォーマンス最適化）",[253,284,285,290,293],{},[271,286,287],{},[274,288,289],{},"public_id",[271,291,292],{},"UUID",[271,294,295],{},"外部公開 ID（API レスポンス、URL の安定性）",[226,297,298,299,302,303,305],{},"なぜ2つ要るのか——BIGINT の連番 PK は JOIN が速く、外部キーの複合構成 (",[274,300,301],{},"(tenant_id, parent_id)",") にも向いています。一方で連番 ID をそのまま API に晒すと、テナント間でのリソース総数（≒ 事業規模）が推測できてしまったり、他社の似た採番から自社の連番を逆算されたりします。UUID の ",[274,304,289],{}," を外部公開用に分離することで、内部実装の効率と外部インターフェースの安全性を両立させています。",[226,307,308,311],{},[230,309,310],{},"データフロー",":",[313,314,315,325],"ul",{},[316,317,318,321,322,324],"li",{},[230,319,320],{},"Read",": JOIN で ",[274,323,289],{}," を直接取得 → そのまま API レスポンスに使う",[316,326,327,330,331,334],{},[230,328,329],{},"Write",": API が受け取った UUID → ",[274,332,333],{},"IdResolver"," が BIGINT に変換 → INSERT/UPDATE で使う",[336,337,342],"pre",{"className":338,"code":340,"language":341},[339],"language-text","Forward:  API (UUID) → Service → IdResolverService → IdResolver → DB (BIGINT)\nRead:     DB (JOIN) → Mapper → Domain (UUID via JOIN columns)\n","text",[274,343,340],{"__ignoreMap":344},"",[226,346,347,349,350,353,354,357],{},[274,348,333],{}," は UUID→BIGINT 変換をリクエストスコープキャッシュ + グローバル Caffeine キャッシュの2段でキャッシュします。テナント分離は3重に守られています——",[274,351,352],{},"resolve"," に渡す ",[274,355,356],{},"tenantId"," パラメータ自体、複合 FK 制約（クロステナント参照を DB レベルで拒否）、そして防御的な RLS ポリシー。",[226,359,360,361,364,365,368,369,372,373,376],{},"新しいエンティティ型を ",[274,362,363],{},"idResolver.register\u003CT>()"," する場合は、テナントコンテキストが確立していることを必ず確認する必要があります（",[274,366,367],{},"User"," と ",[274,370,371],{},"Tenant"," だけがテナントに属さないグローバルなエンティティで、この確認が要りません）。確認を怠ると init/setup コードから呼んだときに ",[274,374,375],{},"RLSContextException"," が飛びます。",[378,379,381,384],"h3",{"id":380},"entityidt-型安全なラッパー",[274,382,383],{},"EntityId\u003CT>"," — 型安全なラッパー",[226,386,387,388,390,391,393],{},"生の ",[274,389,292],{}," をそのままドメインモデルのフィールドに置くことは規約違反です。すべての ID は ",[274,392,383],{}," という値クラスでラップします。",[336,395,399],{"className":396,"code":397,"language":398,"meta":344,"style":344},"language-kotlin shiki shiki-themes github-light github-dark","@JvmInline\n@Serializable\nvalue class EntityId\u003CT>(@Contextual val value: UUID) {\n    override fun toString(): String = value.toString()\n}\n","kotlin",[274,400,401,409,415,421,427],{"__ignoreMap":344},[402,403,406],"span",{"class":404,"line":405},"line",1,[402,407,408],{},"@JvmInline\n",[402,410,412],{"class":404,"line":411},2,[402,413,414],{},"@Serializable\n",[402,416,418],{"class":404,"line":417},3,[402,419,420],{},"value class EntityId\u003CT>(@Contextual val value: UUID) {\n",[402,422,424],{"class":404,"line":423},4,[402,425,426],{},"    override fun toString(): String = value.toString()\n",[402,428,430],{"class":404,"line":429},5,[402,431,432],{},"}\n",[226,434,435,438,439,442,443,446,447,450,451,453,454,457,458,461],{},[274,436,437],{},"T"," によって ",[274,440,441],{},"TagId"," や ",[274,444,445],{},"TenantId"," のような別々の型になるので、",[274,448,449],{},"tagId"," を誤って ",[274,452,356],{}," の引数に渡すようなミスはコンパイル時に弾かれます。命名規約は ",[274,455,456],{},"{entity}Id","（UUID・外部公開）/ ",[274,459,460],{},"internal{Entity}Id","（BIGINT・インフラ層限定）です。",[239,463,465],{"id":464},"jsonb-柔軟スキーマテーブル","JSONB 柔軟スキーマテーブル",[226,467,468,469,472],{},"Astar の中核機能である「テーブル」は、固定スキーマの RDB テーブルではなく ",[230,470,471],{},"JSONB で定義を持つ動的テーブル","です（Notion のデータベースに近いモデル）。",[336,474,478],{"className":475,"code":476,"language":477,"meta":344,"style":344},"language-sql shiki shiki-themes github-light github-dark","CREATE TABLE public.tables (\n    id               BIGINT GENERATED ALWAYS AS IDENTITY,\n    public_id        UUID NOT NULL DEFAULT gen_random_uuid(),\n    tenant_id        BIGINT NOT NULL,\n    workspace_id     BIGINT NOT NULL,\n    name             VARCHAR(255) NOT NULL,\n    properties       JSONB NOT NULL DEFAULT '{}'::jsonb,   -- PropertyDefinition の配列\n    property_order   TEXT[] NOT NULL DEFAULT '{}'::text[],\n    organize_config  JSONB,\n    ...\n);\n\nCREATE TABLE public.records (\n    id          BIGINT GENERATED ALWAYS AS IDENTITY,\n    public_id   UUID NOT NULL DEFAULT gen_random_uuid(),\n    tenant_id   BIGINT NOT NULL,\n    table_id    BIGINT NOT NULL,\n    data        JSONB NOT NULL DEFAULT '{}'::jsonb,        -- 実データ\n    position    DOUBLE PRECISION NOT NULL DEFAULT 65536,\n    ...\n);\n","sql",[274,479,480,485,490,495,500,505,511,517,523,529,535,541,548,554,560,566,572,578,584,590,595],{"__ignoreMap":344},[402,481,482],{"class":404,"line":405},[402,483,484],{},"CREATE TABLE public.tables (\n",[402,486,487],{"class":404,"line":411},[402,488,489],{},"    id               BIGINT GENERATED ALWAYS AS IDENTITY,\n",[402,491,492],{"class":404,"line":417},[402,493,494],{},"    public_id        UUID NOT NULL DEFAULT gen_random_uuid(),\n",[402,496,497],{"class":404,"line":423},[402,498,499],{},"    tenant_id        BIGINT NOT NULL,\n",[402,501,502],{"class":404,"line":429},[402,503,504],{},"    workspace_id     BIGINT NOT NULL,\n",[402,506,508],{"class":404,"line":507},6,[402,509,510],{},"    name             VARCHAR(255) NOT NULL,\n",[402,512,514],{"class":404,"line":513},7,[402,515,516],{},"    properties       JSONB NOT NULL DEFAULT '{}'::jsonb,   -- PropertyDefinition の配列\n",[402,518,520],{"class":404,"line":519},8,[402,521,522],{},"    property_order   TEXT[] NOT NULL DEFAULT '{}'::text[],\n",[402,524,526],{"class":404,"line":525},9,[402,527,528],{},"    organize_config  JSONB,\n",[402,530,532],{"class":404,"line":531},10,[402,533,534],{},"    ...\n",[402,536,538],{"class":404,"line":537},11,[402,539,540],{},");\n",[402,542,544],{"class":404,"line":543},12,[402,545,547],{"emptyLinePlaceholder":546},true,"\n",[402,549,551],{"class":404,"line":550},13,[402,552,553],{},"CREATE TABLE public.records (\n",[402,555,557],{"class":404,"line":556},14,[402,558,559],{},"    id          BIGINT GENERATED ALWAYS AS IDENTITY,\n",[402,561,563],{"class":404,"line":562},15,[402,564,565],{},"    public_id   UUID NOT NULL DEFAULT gen_random_uuid(),\n",[402,567,569],{"class":404,"line":568},16,[402,570,571],{},"    tenant_id   BIGINT NOT NULL,\n",[402,573,575],{"class":404,"line":574},17,[402,576,577],{},"    table_id    BIGINT NOT NULL,\n",[402,579,581],{"class":404,"line":580},18,[402,582,583],{},"    data        JSONB NOT NULL DEFAULT '{}'::jsonb,        -- 実データ\n",[402,585,587],{"class":404,"line":586},19,[402,588,589],{},"    position    DOUBLE PRECISION NOT NULL DEFAULT 65536,\n",[402,591,593],{"class":404,"line":592},20,[402,594,534],{},[402,596,598],{"class":404,"line":597},21,[402,599,540],{},[226,601,602,605,606,609,610,613,614,617],{},[274,603,604],{},"tables.properties"," がスキーマ定義（キー: プロパティキー、値: ",[274,607,608],{},"PropertyDefinition","）、",[274,611,612],{},"records.data"," が実際のレコードデータです。GIN インデックスで JSONB 内の高速検索を、",[274,615,616],{},"record_embeddings","（pgvector + HNSW インデックス）でセマンティック検索を提供しています。",[226,619,620,621,624,625,628,629,632,633,636,637,639],{},"プロパティ型（テキスト・数値・日付・選択肢など）のバリデーション・スキーマ・UI 設定は ",[274,622,623],{},"PropertyType.kt"," の ",[274,626,627],{},"configMetadata()"," を唯一の SSoT としていて、",[274,630,631],{},"PropertyDefinition.validate()"," はそこに委譲する薄いアダプタです。新しいプロパティ型を足すときに ",[274,634,635],{},"when (type)"," 分岐をあちこちに書き散らすのは規約違反で、",[274,638,627],{}," の1箇所を編集すれば、JSON Schema やフロントの設定型は自動生成されます。",[239,641,643],{"id":642},"exposed-ormjpahibernate-ではない","Exposed ORM（JPA/Hibernate ではない）",[226,645,646,647,654],{},"データアクセス層は ",[648,649,653],"a",{"href":650,"rel":651},"https://github.com/JetBrains/Exposed",[652],"nofollow","Exposed","（JetBrains 製）です。Spring Data JDBC からの移行は完了していて、JPA/Hibernate は使っていません。他の Spring Boot プロジェクトの経験や、社外チュートリアルの JPA 前提の知識はほとんど当てはまらないので注意してください。",[226,656,657,658,661,662,665,666,669],{},"テーブル定義は Kotlin の ",[274,659,660],{},"object","（Exposed table object）が SSoT です。新しいテーブルやカラムを追加するときは、まず Exposed table object を編集し、そこから ",[274,663,664],{},"./gradlew generateMigration"," で Flyway マイグレーション SQL を生成します（RLS ポリシーは自動付与）。詳しくは ",[648,667,668],{"href":85},"バックエンドガイド／マイグレーション"," を参照してください。",[239,671,672],{"id":672},"参照実装",[226,674,675,678,679,681,682,685],{},[274,676,677],{},"core/tag/"," が規約に最も忠実な、最小の完全なモジュールです。Dual-ID・",[274,680,383],{},"・Exposed のパターンを実コードで確認したいときは、まずここを読んでください。もう少し複雑な実例（ports/adapters・非同期処理・8つの feature package 分割）を見たい場合は ",[274,683,684],{},"core/organize/"," が参考になります。",[687,688,689],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":344,"searchDepth":411,"depth":411,"links":691},[692,696,697,698],{"id":241,"depth":411,"text":242,"children":693},[694],{"id":380,"depth":417,"text":695},"EntityId\u003CT> — 型安全なラッパー",{"id":464,"depth":411,"text":465},{"id":642,"depth":411,"text":643},{"id":672,"depth":411,"text":672},"dual-ID 規約 (public_id UUID vs internal BIGINT id) と IdResolver、JSONB 柔軟スキーマテーブル、Exposed ORM（JPA/Hibernate ではない）。","md",{},{"title":60},{"title":60,"description":699},"KF-d0d6UFmexpIFtTLasdNpcImnHsf4xZ-ycURiOsmI",[706,708,709,711,712,713,714,715,716,717,718,719,721,722,723,724,725,727,728,729,730,731,733,735,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,755,756,757,758],{"path":13,"title":707},"Astar とは何か（5分で）",{"path":17,"title":16},{"path":6,"title":710},"ようこそ",{"path":29,"title":28},{"path":33,"title":32},{"path":37,"title":36},{"path":21,"title":25},{"path":49,"title":48},{"path":53,"title":52},{"path":57,"title":56},{"path":61,"title":60},{"path":41,"title":720},"システムアーキテクチャ",{"path":73,"title":72},{"path":77,"title":76},{"path":81,"title":80},{"path":85,"title":84},{"path":65,"title":726},"バックエンドガイド",{"path":97,"title":96},{"path":101,"title":100},{"path":105,"title":104},{"path":109,"title":108},{"path":89,"title":732},"フロントエンドガイド",{"path":121,"title":734},"Go agent — NAS 同期エージェント",{"path":125,"title":736},"astar CLI",{"path":113,"title":117},{"path":137,"title":136},{"path":141,"title":140},{"path":145,"title":144},{"path":149,"title":148},{"path":153,"title":152},{"path":157,"title":156},{"path":161,"title":160},{"path":165,"title":164},{"path":169,"title":168},{"path":173,"title":172},{"path":177,"title":176},{"path":181,"title":180},{"path":185,"title":184},{"path":189,"title":188},{"path":193,"title":192},{"path":129,"title":754},"モジュール索引",{"path":205,"title":204},{"path":209,"title":208},{"path":197,"title":201},{"path":213,"title":759},"docs/ 地図",1785452454969]