[{"data":1,"prerenderedAt":810},["ShallowReactive",2],{"navigation_docs_en":3,"-en-getting-started-project-structure":342,"-en-getting-started-project-structure-surround":805},[4,45,61,96,132,166,196,217,241,265],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F10.reference",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F10.reference\u002F1.configuration","i-lucide-settings",{"title":57,"path":58,"stem":59,"icon":60},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F10.reference\u002F2.dependencies","i-lucide-package",{"title":62,"icon":63,"path":64,"stem":65,"children":66,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[67,72,77,82,86,91],{"title":68,"path":69,"stem":70,"icon":71},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture","i-lucide-layers",{"title":73,"path":74,"stem":75,"icon":76},"Application lifecycle","\u002Fen\u002Fconcepts\u002Fapplication-lifecycle","en\u002F2.concepts\u002F2.application-lifecycle","i-lucide-power",{"title":78,"path":79,"stem":80,"icon":81},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F3.error-model","i-lucide-shield-alert",{"title":52,"path":83,"stem":84,"icon":85},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F4.configuration","i-lucide-settings-2",{"title":87,"path":88,"stem":89,"icon":90},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F5.codegen-pipeline","i-lucide-file-json",{"title":92,"path":93,"stem":94,"icon":95},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F6.design-patterns","i-lucide-puzzle",{"title":97,"icon":98,"path":99,"stem":100,"children":101,"page":44},"HTTP & Routing","i-lucide-globe","\u002Fen\u002Fhttp","en\u002F3.http",[102,107,112,117,122,127],{"title":103,"path":104,"stem":105,"icon":106},"The server core","\u002Fen\u002Fhttp\u002Fserver","en\u002F3.http\u002F1.server","i-lucide-server",{"title":108,"path":109,"stem":110,"icon":111},"The Fiber engine","\u002Fen\u002Fhttp\u002Ffiber","en\u002F3.http\u002F2.fiber","i-lucide-zap",{"title":113,"path":114,"stem":115,"icon":116},"The chi engine","\u002Fen\u002Fhttp\u002Fchi","en\u002F3.http\u002F3.chi","i-lucide-route",{"title":118,"path":119,"stem":120,"icon":121},"Error responses","\u002Fen\u002Fhttp\u002Ferror-responses","en\u002F3.http\u002F4.error-responses","i-lucide-octagon-alert",{"title":123,"path":124,"stem":125,"icon":126},"Validation","\u002Fen\u002Fhttp\u002Fvalidation","en\u002F3.http\u002F5.validation","i-lucide-badge-check",{"title":128,"path":129,"stem":130,"icon":131},"Pagination","\u002Fen\u002Fhttp\u002Fpagination","en\u002F3.http\u002F6.pagination","i-lucide-list-ordered",{"title":133,"icon":134,"path":135,"stem":136,"children":137,"page":44},"Database","i-lucide-database","\u002Fen\u002Fdatabase","en\u002F4.database",[138,142,147,151,156,161],{"title":139,"path":140,"stem":141,"icon":134},"Overview","\u002Fen\u002Fdatabase\u002Foverview","en\u002F4.database\u002F1.overview",{"title":143,"path":144,"stem":145,"icon":146},"pgx","\u002Fen\u002Fdatabase\u002Fpgx","en\u002F4.database\u002F2.pgx","i-lucide-plug",{"title":148,"path":149,"stem":150,"icon":71},"bun","\u002Fen\u002Fdatabase\u002Fbun","en\u002F4.database\u002F3.bun",{"title":152,"path":153,"stem":154,"icon":155},"Transactions","\u002Fen\u002Fdatabase\u002Ftransactions","en\u002F4.database\u002F4.transactions","i-lucide-git-merge",{"title":157,"path":158,"stem":159,"icon":160},"Migrations","\u002Fen\u002Fdatabase\u002Fmigrations","en\u002F4.database\u002F5.migrations","i-lucide-file-stack",{"title":162,"path":163,"stem":164,"icon":165},"Seeders","\u002Fen\u002Fdatabase\u002Fseeding","en\u002F4.database\u002F6.seeding","i-lucide-sprout",{"title":167,"icon":168,"path":169,"stem":170,"children":171,"page":44},"Async & Background","i-lucide-workflow","\u002Fen\u002Fasync","en\u002F5.async",[172,176,181,186,191],{"title":173,"path":174,"stem":175,"icon":131},"Queue","\u002Fen\u002Fasync\u002Fqueue","en\u002F5.async\u002F1.queue",{"title":177,"path":178,"stem":179,"icon":180},"Events","\u002Fen\u002Fasync\u002Fevents","en\u002F5.async\u002F2.events","i-lucide-radio",{"title":182,"path":183,"stem":184,"icon":185},"Outbox","\u002Fen\u002Fasync\u002Foutbox","en\u002F5.async\u002F3.outbox","i-lucide-inbox",{"title":187,"path":188,"stem":189,"icon":190},"Scheduling","\u002Fen\u002Fasync\u002Fscheduler","en\u002F5.async\u002F4.scheduler","i-lucide-calendar-clock",{"title":192,"path":193,"stem":194,"icon":195},"Worker","\u002Fen\u002Fasync\u002Fworker","en\u002F5.async\u002F5.worker","i-lucide-cog",{"title":197,"icon":198,"path":199,"stem":200,"children":201,"page":44},"Security","i-lucide-shield-check","\u002Fen\u002Fsecurity","en\u002F6.security",[202,207,212],{"title":203,"path":204,"stem":205,"icon":206},"Authentication","\u002Fen\u002Fsecurity\u002Fauthentication","en\u002F6.security\u002F1.authentication","i-lucide-key-round",{"title":208,"path":209,"stem":210,"icon":211},"RBAC","\u002Fen\u002Fsecurity\u002Frbac","en\u002F6.security\u002F2.rbac","i-lucide-users",{"title":213,"path":214,"stem":215,"icon":216},"Policies","\u002Fen\u002Fsecurity\u002Fpolicies","en\u002F6.security\u002F3.policies","i-lucide-gavel",{"title":218,"icon":219,"path":220,"stem":221,"children":222,"page":44},"Observability","i-lucide-activity","\u002Fen\u002Fobservability","en\u002F7.observability",[223,226,231,236],{"title":139,"path":224,"stem":225,"icon":219},"\u002Fen\u002Fobservability\u002Foverview","en\u002F7.observability\u002F1.overview",{"title":227,"path":228,"stem":229,"icon":230},"Logging","\u002Fen\u002Fobservability\u002Flogging","en\u002F7.observability\u002F2.logging","i-lucide-scroll-text",{"title":232,"path":233,"stem":234,"icon":235},"OpenTelemetry","\u002Fen\u002Fobservability\u002Fopentelemetry","en\u002F7.observability\u002F3.opentelemetry","i-lucide-radar",{"title":237,"path":238,"stem":239,"icon":240},"Sentry","\u002Fen\u002Fobservability\u002Fsentry","en\u002F7.observability\u002F4.sentry","i-lucide-bug",{"title":242,"icon":243,"path":244,"stem":245,"children":246,"page":44},"Storage & Mail","i-lucide-hard-drive","\u002Fen\u002Fstorage-mail","en\u002F8.storage-mail",[247,251,256,261],{"title":248,"path":249,"stem":250,"icon":243},"Storage","\u002Fen\u002Fstorage-mail\u002Fstorage","en\u002F8.storage-mail\u002F1.storage",{"title":252,"path":253,"stem":254,"icon":255},"Email","\u002Fen\u002Fstorage-mail\u002Fmail","en\u002F8.storage-mail\u002F2.mail","i-lucide-mail",{"title":257,"path":258,"stem":259,"icon":260},"Notifications","\u002Fen\u002Fstorage-mail\u002Fnotifications","en\u002F8.storage-mail\u002F3.notifications","i-lucide-bell",{"title":262,"path":263,"stem":264,"icon":180},"Realtime","\u002Fen\u002Fstorage-mail\u002Frealtime","en\u002F8.storage-mail\u002F4.realtime",{"title":266,"icon":267,"path":268,"stem":269,"children":270,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F9.cli",[271,274,279,284,289,294,299,303,308,313,318,323,328,333,337],{"title":139,"path":272,"stem":273,"icon":267},"\u002Fen\u002Fcli\u002Foverview","en\u002F9.cli\u002F1.overview",{"title":275,"path":276,"stem":277,"icon":278},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F9.cli\u002F10.add-db","i-lucide-database-zap",{"title":280,"path":281,"stem":282,"icon":283},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F9.cli\u002F11.add-compose","i-lucide-container",{"title":285,"path":286,"stem":287,"icon":288},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F9.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":290,"path":291,"stem":292,"icon":293},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F9.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":295,"path":296,"stem":297,"icon":298},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F9.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":300,"path":301,"stem":302,"icon":165},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F9.cli\u002F15.add-seeder",{"title":304,"path":305,"stem":306,"icon":307},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F9.cli\u002F2.new-project","i-lucide-folder-plus",{"title":309,"path":310,"stem":311,"icon":312},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F9.cli\u002F3.new-module","i-lucide-blocks",{"title":314,"path":315,"stem":316,"icon":317},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F9.cli\u002F4.add-surface","i-lucide-layers-2",{"title":319,"path":320,"stem":321,"icon":322},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F9.cli\u002F5.add-core","i-lucide-box",{"title":324,"path":325,"stem":326,"icon":327},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F9.cli\u002F6.add-handler","i-lucide-webhook",{"title":329,"path":330,"stem":331,"icon":332},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F9.cli\u002F7.new-migration","i-lucide-file-plus",{"title":334,"path":335,"stem":336,"icon":195},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F9.cli\u002F8.worker-generators",{"title":338,"path":339,"stem":340,"icon":341},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F9.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"id":343,"title":35,"body":344,"description":798,"extension":799,"links":800,"meta":801,"navigation":802,"path":36,"seo":803,"stem":37,"__hash__":804},"docs_en\u002Fen\u002F1.getting-started\u002F6.project-structure.md",{"type":345,"value":346,"toc":790},"minimark",[347,356,367,389,394,397,508,545,577,603,607,610,616,698,702,726,730,748,759,763],[348,349,350,351,355],"p",{},"This is what the ",[352,353,354],"a",{"href":41},"shop sample application"," looks like fully built out: two surfaces, a worker, events and migrations:",[357,358,363],"pre",{"className":359,"code":361,"language":362},[360],"language-text","shop\u002F\n├── gpsystem.yaml                 # generator manifest: project name, module path, kit version,\n│                                 # engine (fiber|chi), db (pgx|bun), template checksum, modules+surfaces\n├── go.mod                        # require gpsystem + tool directives (gpsystem, oapi-codegen)\n├── mise.toml                     # tasks: setup \u002F spec \u002F generate \u002F dev \u002F restart \u002F build \u002F test\n├── Dockerfile                    # 4 stages: base \u002F development (bind mount, go run) \u002F build \u002F production\n├── .dockerignore\n├── compose.yml                   # full dev stack: postgres, redis, mailpit, rustfs, migrate,\n│                                 # worker, module services, zero published ports, Traefik\u002Fproxynet\n├── .env.example\n├── spec\u002Ftypespec\u002F                # ★ the API contract (source of truth)\n│   ├── main.tsp                  # imports every module (anchor-managed)\n│   ├── lib\u002F                      # the @permission \u002F @policy decorators\n│   ├── shared\u002Ferrors.tsp         # ProblemDetail + FieldError (mirror of httperr)\n│   ├── shared\u002Fpaginator.tsp\n│   └── modules\u002F\u003Cmodule>\u002F         # per module: \u003Csurface>.tsp + models\u002F\n├── api\u002F\n│   ├── openapi\u002F                  # emitted OpenAPI 3.0 (committed, for reviewable diffs)\n│   └── oapi-codegen\u002F\n│       ├── templates\u002F            # the project's codegen templates (Fiber: full set;\n│       │                         # chi: minimal strict overrides)\n│       └── \u003Cmodule>-\u003Csurface>.yaml\n├── cmd\u002F\n│   ├── \u003Cmodule>\u002Fmain.go          # one HTTP entry point per module (~20 lines)\n│   ├── worker\u002Fmain.go            # background processing: asynq server + outbox relay + scheduler\n│   └── migrate\u002Fmain.go           # migration runner\n├── internal\u002F\n│   ├── platform\u002Fconfig\u002Fconfig.go # composed env config (Server + DB + Worker + Outbox [+ JWT])\n│   └── modules\u002F\u003Cmodule>\u002F\n│       ├── register.go           # Dependencies struct + one Register\u003CSurface>(router, deps) per surface\n│       ├── core\u002F                 # the module's SHARED business logic: entities\u002Fviews, rules,\n│       │                         # sentinel errors (e.g. ErrNotFound); every surface uses it\n│       ├── repository\u002F           # THE module's single persistence layer (row structs +\n│       │                         # interface + implementation); repository\u002F\u003Cconn>\u002F for a named connection\n│       ├── events\u002F               # the module's events (struct + EventName)\n│       ├── listeners\u002F            # the module's worker listeners\n│       │   ├── register.go       # events.Listen(...) registrations for the worker\n│       │   └── \u003Cname>.go         # listener bodies\n│       ├── jobs\u002F                 # the module's worker jobs\n│       │   ├── register.go       # sched.Job(...) registrations for the worker\n│       │   └── \u003Cname>.go         # job bodies\n│       ├── mail\u002F                 # the module's mail notifiers (payload + templates)\n│       │   └── \u003Cname>.go         # notifier body + templates\u002F\u003Cname>.{html,txt}.tmpl\n│       ├── notifications\u002F        # the module's multi-channel notifications (payload + Via)\n│       │   └── \u003Cname>.go         # notification body + templates\u002F\u003Cname>.{html,txt}.tmpl\n│       └── surfaces\u002F\n│           └── \u003Csurface>\u002F        # any name (api | web | admin | ...); \"api\" mounts at the module root\n│               ├── http\u002Fhandler.go   # implements the generated strict interface\n│               ├── http\u002Fmapper\u002F      # DTO ↔ service type mapping (kept empty)\n│               ├── http\u002Fgen\u002F         # oapi-codegen output, never edit\n│               ├── policy\u002F           # policy registry (enforcing @permission\u002F@policy)\n│               └── service\u002F          # thin audience-specific seam: delegates to core\n└── migrations\u002F                   # timestamp-prefixed SQL migrations (20200101000000_init.sql, 20200101000100_outbox.sql, ...)\n","text",[364,365,361],"code",{"__ignoreMap":366},"",[348,368,369,370,373,374,373,377,373,380,383,384,388],{},"The worker layer (",[364,371,372],{},"cmd\u002Fworker",", ",[364,375,376],{},"listeners\u002F",[364,378,379],{},"jobs\u002F",[364,381,382],{},"events\u002F",") is included in new projects by default; ",[352,385,387],{"href":386},"\u002Fen\u002Fcli\u002Fworker-generators#add-worker","add worker"," retrofits it into older ones.",[390,391,393],"h2",{"id":392},"what-can-live-inside-a-module","What can live inside a module",[348,395,396],{},"A module folder holds four distinct kinds of thing, worth naming explicitly since they're easy to conflate:",[398,399,400,416],"table",{},[401,402,403],"thead",{},[404,405,406,410,413],"tr",{},[407,408,409],"th",{},"Kind",[407,411,412],{},"Folders",[407,414,415],{},"What it is",[417,418,419,447,469,485],"tbody",{},[404,420,421,425,434],{},[422,423,424],"td",{},"Surfaces",[422,426,427,373,430,433],{},[364,428,429],{},"surfaces\u002Fapi\u002F",[364,431,432],{},"surfaces\u002Fadmin\u002F",", ...",[422,435,436,437,440,441,440,444],{},"Parallel per-audience HTTP bundles: ",[364,438,439],{},"http"," + ",[364,442,443],{},"service",[364,445,446],{},"policy",[404,448,449,452,460],{},[422,450,451],{},"Shared core",[422,453,454,373,457],{},[364,455,456],{},"core\u002F",[364,458,459],{},"repository\u002F",[422,461,462,463,465,466,468],{},"What every surface shares: the business rules and entities (",[364,464,456],{},") plus the module's single persistence layer (",[364,467,459],{},")",[404,470,471,474,480],{},[422,472,473],{},"Trigger units",[422,475,476,373,478],{},[364,477,379],{},[364,479,376],{},[422,481,482,483],{},"Alternative entry points: invoked by time or by an event instead of HTTP; wired into ",[364,484,372],{},[404,486,487,490,500],{},[422,488,489],{},"Contracts & content",[422,491,492,373,494,373,497],{},[364,493,382],{},[364,495,496],{},"mail\u002F",[364,498,499],{},"notifications\u002F",[422,501,502,503,507],{},"What the module ",[504,505,506],"em",{},"produces",": event payloads, message content and templates",[509,510,511,512,516,517,520,521,524,525,528,529,532,533,535,536,539,540,544],"note",{},"A ",[513,514,515],"strong",{},"surface"," is a per-audience bundle: a public ",[364,518,519],{},"api",", an ",[364,522,523],{},"admin",", a ",[364,526,527],{},"web",", each a parallel HTTP face of the same module. ",[513,530,531],{},"Core"," is the module's shared business logic (plain structs with methods, rules written once), not a DDD \"domain layer\". The per-surface ",[513,534,443],{}," is the audience-specific seam: in DDD terms it plays the ",[504,537,538],{},"application service"," role and delegates shared rules to core. See ",[352,541,543],{"href":542},"\u002Fen\u002Fconcepts\u002Fdesign-patterns#service-layer","design patterns"," for the reasoning.",[348,546,547,548,561,562,373,565,568,569,572,573,576],{},"The rule of thumb: ",[513,549,550,551,554,555,557,558,560],{},"channel\u002Fdriver code lives in the kit or in ",[364,552,553],{},"internal\u002Fplatform",", content lives in the module, triggers live in ",[364,556,379],{},"\u002F",[364,559,376],{},"."," Mail is the clearest example: the SMTP transport (",[364,563,564],{},"gpsystem\u002Fmail",[364,566,567],{},"gpsystem\u002Fmail\u002Fsmtp",") is infrastructure, exactly like the database driver, it lives in the kit. What email to send, with what subject and template, is the contact module's business content: it lives in ",[364,570,571],{},"internal\u002Fmodules\u002Fcontact\u002Fmail\u002F",". See ",[352,574,575],{"href":253},"mail"," for the worked example.",[348,578,579,580,583,584,587,588,591,592,595,596,599,600,560],{},"The same split applies one level up to ",[352,581,582],{"href":258},"notifications",": a notification's ",[504,585,586],{},"channels"," (mail, database, broadcast) are kit-provided (",[364,589,590],{},"gpsystem\u002Fnotify",", plus ",[364,593,594],{},"gpsystem\u002Fnotify\u002Fdatabase"," and ",[364,597,598],{},"gpsystem\u002Fnotify\u002Fbroadcast"," for the driver-specific pieces), while what a notification says (its payload, subject, and templates) is module content in ",[364,601,602],{},"internal\u002Fmodules\u002F\u003Cmodule>\u002Fnotifications\u002F",[390,604,606],{"id":605},"layer-rules","Layer rules",[348,608,609],{},"The dependency direction is one-way:",[357,611,614],{"className":612,"code":613,"language":362},[360],"http (handler) → service → core → repository\n",[364,615,613],{"__ignoreMap":366},[617,618,619,631,645,661,688],"ul",{},[620,621,622,623,626,627,630],"li",{},"The handler never touches the database; it binds\u002Fvalidates input, calls its own surface's service and maps the result. ",[513,624,625],{},"Handlers never import the repository package",": not-found and similar sentinels are defined in the repository or in core and re-exported through core\u002Fservice, so a handler only ever checks its own surface's service package (",[364,628,629],{},"errors.Is(err, service.ErrNotFound)",").",[620,632,633,634,636,637,640,641,644],{},"The per-surface ",[364,635,443],{}," is a thin, audience-specific seam: it delegates shared rules to ",[364,638,639],{},"core"," and keeps only its own audience's flows. It depends on repository ",[513,642,643],{},"interfaces",": testable with plain fakes.",[620,646,647,649,650,653,654,657,658],{},[364,648,639],{}," is the module's shared business logic: the entities\u002Fviews, the rules written once, and the sentinel errors. Events are dispatched where the rule lives, ",[352,651,652],{"href":153},"inside the transaction",", through the dispatcher received in ",[364,655,656],{},"Dependencies",". ",[513,659,660],{},"Core never imports a surface package.",[620,662,663,664,666,667,672,673,676,677,679,680,683,684,630],{},"The module-level ",[364,665,459],{}," contains the SQL, the row structs and the interface + implementation, once per module, shared by every surface. It receives a ",[352,668,669],{"href":144},[364,670,671],{},"pg.DBTX",", so the same code runs on the pool or in a transaction without knowing it, or, in a ",[364,674,675],{},"--db bun"," project, the ",[352,678,148],{"href":149}," counterpart. A module's repository uses one DB engine; for a named connection it splits into a ",[364,681,682],{},"repository\u002F\u003Cconn>\u002F"," subfolder (",[352,685,686],{"href":276},[364,687,275],{},[620,689,690,693,694,697],{},[364,691,692],{},"policy\u002F"," holds the surface's request-level authorization rules: the ",[352,695,696],{"href":214},"policy layer"," calls them through the generated enforcer middleware.",[390,699,701],{"id":700},"two-binaries-one-lifecycle","Two binaries, one lifecycle",[348,703,704,707,708,710,711,717,718,721,722,725],{},[364,705,706],{},"cmd\u002F\u003Cmodule>"," runs the HTTP side, ",[364,709,372],{}," the background processing; both build on the same ",[352,712,713,716],{"href":74},[364,714,715],{},"app"," lifecycle",": identical graceful shutdown, identical telemetry wiring. You scale them separately: more HTTP replicas, more worker replicas. The ",[352,719,720],{"href":183},"outbox"," and the ",[352,723,724],{"href":188},"scheduler"," are replica-safe.",[390,727,729],{"id":728},"generated-vs-yours","Generated vs. yours",[348,731,732,733,736,737,740,741,744,745,560],{},"Only ",[364,734,735],{},"http\u002Fgen\u002F"," is regenerated output (and marked as such). Everything else (handlers, services, repositories, the ",[364,738,739],{},".tsp"," files, listener\u002Fjob bodies) is normal code you edit. The generators only extend existing files at ",[364,742,743],{},"\u002F\u002F gpsystem:*"," anchor comments; see the ",[352,746,747],{"href":272},"CLI overview",[509,749,750,751,754,755,758],{},"The ",[364,752,753],{},"api\u002Fopenapi\u002F*.yaml"," files are committed on purpose: API changes show up as reviewable diffs in pull requests, and ",[364,756,757],{},"go build"," never depends on the Node toolchain.",[390,760,762],{"id":761},"where-does-shared-code-go","Where does shared code go?",[348,764,765,766,769,770,773,774,777,778,781,782,784,785,789],{},"There is no ",[364,767,768],{},"pkg\u002F"," and no ",[364,771,772],{},"internal\u002Fpkg\u002F"," in a generated project: ",[364,775,776],{},"internal\u002F"," already means private, and a second layer under it just invites a junk drawer. ",[364,779,780],{},"internal\u002Fplatform\u002Fconfig\u002F"," is the only app-wide shared folder a fresh project ships with. Inside a module, rules shared by several surfaces belong in the generated ",[364,783,456],{},"; see ",[352,786,788],{"href":787},"\u002Fen\u002Fconcepts\u002Farchitecture#shared-code-a-decision-ladder","Shared code: a decision ladder"," for where to put code used by more than one surface or module.",{"title":366,"searchDepth":791,"depth":791,"links":792},2,[793,794,795,796,797],{"id":392,"depth":791,"text":393},{"id":605,"depth":791,"text":606},{"id":700,"depth":791,"text":701},{"id":728,"depth":791,"text":729},{"id":761,"depth":791,"text":762},"The layout of a generated consumer project and the rules behind it.","md",null,{},{"icon":38},{"title":35,"description":798},"_CQ0-dev9S6TqDxYj1v_hM-HOWZnNf_ZsVRnbUpHUUI",[806,808],{"title":30,"path":31,"stem":32,"description":807,"icon":33,"children":-1},"Generate a module, run the codegen chain and boot the service.",{"title":40,"path":41,"stem":42,"description":809,"icon":43,"children":-1},"A single worked example project, every code sample in the docs builds on it.",1784668705437]