[{"data":1,"prerenderedAt":4913},["ShallowReactive",2],{"navigation_docs_hu":3,"-hu-concepts-design-patterns":442,"-hu-concepts-design-patterns-surround":4908},[4,45,60,81,112,129,146,173,197,213,242,280,357,373,385,404,430],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Kezdő lépések","i-lucide-rocket","\u002Fhu\u002Fgetting-started","hu\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Bevezetés","\u002Fhu\u002Fgetting-started\u002Fintroduction","hu\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Laravelből érkezőknek","\u002Fhu\u002Fgetting-started\u002Fcoming-from-laravel","hu\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Symfonyból érkezőknek","\u002Fhu\u002Fgetting-started\u002Fcoming-from-symfony","hu\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Telepítés","\u002Fhu\u002Fgetting-started\u002Finstallation","hu\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Az első modulod","\u002Fhu\u002Fgetting-started\u002Ffirst-module","hu\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Projektstruktúra","\u002Fhu\u002Fgetting-started\u002Fproject-structure","hu\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"A shop mintaalkalmazás","\u002Fhu\u002Fgetting-started\u002Fsample-app","hu\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"queue","i-lucide-layers","\u002Fhu\u002Fqueue","hu\u002F10.queue",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Áttekintés","\u002Fhu\u002Fqueue\u002Foverview","hu\u002F10.queue\u002F1.overview","i-lucide-list-ordered",{"title":57,"path":58,"stem":59,"icon":47},"Taskok","\u002Fhu\u002Fqueue\u002Ftasks","hu\u002F10.queue\u002F2.tasks",{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":44},"events","i-lucide-workflow","\u002Fhu\u002Fevents","hu\u002F11.events",[66,71,76],{"title":67,"path":68,"stem":69,"icon":70},"Események","\u002Fhu\u002Fevents\u002Foverview","hu\u002F11.events\u002F1.overview","i-lucide-radio",{"title":72,"path":73,"stem":74,"icon":75},"Outbox","\u002Fhu\u002Fevents\u002Foutbox","hu\u002F11.events\u002F2.outbox","i-lucide-inbox",{"title":77,"path":78,"stem":79,"icon":80},"Ütemezés","\u002Fhu\u002Fevents\u002Fscheduler","hu\u002F11.events\u002F3.scheduler","i-lucide-calendar-clock",{"title":82,"icon":83,"path":84,"stem":85,"children":86,"page":44},"auth","i-lucide-shield-check","\u002Fhu\u002Fauth","hu\u002F12.auth",[87,92,97,102,107],{"title":88,"path":89,"stem":90,"icon":91},"Autentikáció","\u002Fhu\u002Fauth\u002Foverview","hu\u002F12.auth\u002F1.overview","i-lucide-key-round",{"title":93,"path":94,"stem":95,"icon":96},"RBAC","\u002Fhu\u002Fauth\u002Frbac","hu\u002F12.auth\u002F2.rbac","i-lucide-users",{"title":98,"path":99,"stem":100,"icon":101},"Policy-k","\u002Fhu\u002Fauth\u002Fpolicy","hu\u002F12.auth\u002F3.policy","i-lucide-gavel",{"title":103,"path":104,"stem":105,"icon":106},"Az auth önálló használata","\u002Fhu\u002Fauth\u002Fstandalone","hu\u002F12.auth\u002F4.standalone","i-lucide-plug",{"title":108,"path":109,"stem":110,"icon":111},"Social login goth-tal, önállóan","\u002Fhu\u002Fauth\u002Fsocial-standalone","hu\u002F12.auth\u002F5.social-standalone","i-lucide-log-in",{"title":113,"icon":114,"path":115,"stem":116,"children":117,"page":44},"mail","i-lucide-mail","\u002Fhu\u002Fmail","hu\u002F13.mail",[118,121,125],{"title":52,"path":119,"stem":120,"icon":114},"\u002Fhu\u002Fmail\u002Foverview","hu\u002F13.mail\u002F1.overview",{"title":122,"path":123,"stem":124,"icon":114},"SMTP","\u002Fhu\u002Fmail\u002Fsmtp","hu\u002F13.mail\u002F2.smtp",{"title":126,"path":127,"stem":128,"icon":114},"MJML","\u002Fhu\u002Fmail\u002Fmjml","hu\u002F13.mail\u002F3.mjml",{"title":130,"icon":131,"path":132,"stem":133,"children":134,"page":44},"notify","i-lucide-bell","\u002Fhu\u002Fnotify","hu\u002F14.notify",[135,138,142],{"title":52,"path":136,"stem":137,"icon":131},"\u002Fhu\u002Fnotify\u002Foverview","hu\u002F14.notify\u002F1.overview",{"title":139,"path":140,"stem":141,"icon":131},"Database csatorna","\u002Fhu\u002Fnotify\u002Fdatabase","hu\u002F14.notify\u002F2.database",{"title":143,"path":144,"stem":145,"icon":131},"Broadcast csatorna","\u002Fhu\u002Fnotify\u002Fbroadcast","hu\u002F14.notify\u002F3.broadcast",{"title":147,"icon":148,"path":149,"stem":150,"children":151,"page":44},"storage","i-lucide-hard-drive","\u002Fhu\u002Fstorage","hu\u002F15.storage",[152,155,159,163,168],{"title":52,"path":153,"stem":154,"icon":148},"\u002Fhu\u002Fstorage\u002Foverview","hu\u002F15.storage\u002F1.overview",{"title":156,"path":157,"stem":158},"Disk API","\u002Fhu\u002Fstorage\u002Fdisk-api","hu\u002F15.storage\u002F2.disk-api",{"title":160,"path":161,"stem":162,"icon":148},"Memória és helyi lemez","\u002Fhu\u002Fstorage\u002Flocal","hu\u002F15.storage\u002F3.local",{"title":164,"path":165,"stem":166,"icon":167},"S3","\u002Fhu\u002Fstorage\u002Fs3","hu\u002F15.storage\u002F4.s3","i-lucide-cloud",{"title":169,"path":170,"stem":171,"icon":172},"Tesztelés","\u002Fhu\u002Fstorage\u002Ftesting","hu\u002F15.storage\u002F5.testing","i-lucide-flask-conical",{"title":174,"icon":175,"path":176,"stem":177,"children":178,"page":44},"telemetry","i-lucide-activity","\u002Fhu\u002Ftelemetry","hu\u002F16.telemetry",[179,182,187,192],{"title":52,"path":180,"stem":181,"icon":175},"\u002Fhu\u002Ftelemetry\u002Foverview","hu\u002F16.telemetry\u002F1.overview",{"title":183,"path":184,"stem":185,"icon":186},"Logolás","\u002Fhu\u002Ftelemetry\u002Flogging","hu\u002F16.telemetry\u002F2.logging","i-lucide-scroll-text",{"title":188,"path":189,"stem":190,"icon":191},"OpenTelemetry","\u002Fhu\u002Ftelemetry\u002Fopentelemetry","hu\u002F16.telemetry\u002F3.opentelemetry","i-lucide-radar",{"title":193,"path":194,"stem":195,"icon":196},"Sentry","\u002Fhu\u002Ftelemetry\u002Fsentry","hu\u002F16.telemetry\u002F4.sentry","i-lucide-bug",{"title":198,"icon":199,"path":200,"stem":201,"children":202,"page":44},"Referencia","i-lucide-book-open","\u002Fhu\u002Freference","hu\u002F17.reference",[203,208],{"title":204,"path":205,"stem":206,"icon":207},"Konfiguráció","\u002Fhu\u002Freference\u002Fconfiguration","hu\u002F17.reference\u002F1.configuration","i-lucide-settings",{"title":209,"path":210,"stem":211,"icon":212},"Külső függőségek","\u002Fhu\u002Freference\u002Fdependencies","hu\u002F17.reference\u002F2.dependencies","i-lucide-package",{"title":214,"icon":215,"path":216,"stem":217,"children":218,"page":44},"Koncepciók","i-lucide-lightbulb","\u002Fhu\u002Fconcepts","hu\u002F2.concepts",[219,223,228,232,237],{"title":220,"path":221,"stem":222,"icon":47},"Architektúra","\u002Fhu\u002Fconcepts\u002Farchitecture","hu\u002F2.concepts\u002F1.architecture",{"title":224,"path":225,"stem":226,"icon":227},"Hibamodell","\u002Fhu\u002Fconcepts\u002Ferror-model","hu\u002F2.concepts\u002F2.error-model","i-lucide-shield-alert",{"title":204,"path":229,"stem":230,"icon":231},"\u002Fhu\u002Fconcepts\u002Fconfiguration","hu\u002F2.concepts\u002F3.configuration","i-lucide-settings-2",{"title":233,"path":234,"stem":235,"icon":236},"Codegen pipeline","\u002Fhu\u002Fconcepts\u002Fcodegen-pipeline","hu\u002F2.concepts\u002F4.codegen-pipeline","i-lucide-file-json",{"title":238,"path":239,"stem":240,"icon":241},"Design patternek","\u002Fhu\u002Fconcepts\u002Fdesign-patterns","hu\u002F2.concepts\u002F5.design-patterns","i-lucide-puzzle",{"title":243,"icon":244,"path":245,"stem":246,"children":247,"page":44},"A kit","i-lucide-box","\u002Fhu\u002Fkit","hu\u002F3.kit",[248,252,257,262,267,271,276],{"title":249,"path":250,"stem":251,"icon":244},"A kit áttekintése","\u002Fhu\u002Fkit\u002Foverview","hu\u002F3.kit\u002F1.overview",{"title":253,"path":254,"stem":255,"icon":256},"Alkalmazás-életciklus","\u002Fhu\u002Fkit\u002Fapp","hu\u002F3.kit\u002F2.app","i-lucide-power",{"title":258,"path":259,"stem":260,"icon":261},"Szerver","\u002Fhu\u002Fkit\u002Fserver","hu\u002F3.kit\u002F3.server","i-lucide-server",{"title":263,"path":264,"stem":265,"icon":266},"Worker","\u002Fhu\u002Fkit\u002Fworker","hu\u002F3.kit\u002F4.worker","i-lucide-cog",{"title":268,"path":269,"stem":270,"icon":70},"Realtime","\u002Fhu\u002Fkit\u002Frealtime","hu\u002F3.kit\u002F5.realtime",{"title":272,"path":273,"stem":274,"icon":275},"Migrációk","\u002Fhu\u002Fkit\u002Fmigrations","hu\u002F3.kit\u002F6.migrations","i-lucide-file-stack",{"title":277,"path":278,"stem":279,"icon":111},"Social login","\u002Fhu\u002Fkit\u002Fsocial-login","hu\u002F3.kit\u002F7.social-login",{"title":281,"icon":282,"path":283,"stem":284,"children":285,"page":44},"CLI referencia","i-lucide-terminal","\u002Fhu\u002Fcli","hu\u002F4.cli",[286,289,294,299,304,309,314,319,324,329,334,338,343,348,352],{"title":52,"path":287,"stem":288,"icon":282},"\u002Fhu\u002Fcli\u002Foverview","hu\u002F4.cli\u002F1.overview",{"title":290,"path":291,"stem":292,"icon":293},"add db","\u002Fhu\u002Fcli\u002Fadd-db","hu\u002F4.cli\u002F10.add-db","i-lucide-database-zap",{"title":295,"path":296,"stem":297,"icon":298},"add compose \u002F add docker","\u002Fhu\u002Fcli\u002Fadd-compose","hu\u002F4.cli\u002F11.add-compose","i-lucide-container",{"title":300,"path":301,"stem":302,"icon":303},"add mail","\u002Fhu\u002Fcli\u002Fadd-mail","hu\u002F4.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":305,"path":306,"stem":307,"icon":308},"add notification","\u002Fhu\u002Fcli\u002Fadd-notification","hu\u002F4.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":310,"path":311,"stem":312,"icon":313},"add realtime","\u002Fhu\u002Fcli\u002Fadd-realtime","hu\u002F4.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":315,"path":316,"stem":317,"icon":318},"add seeder","\u002Fhu\u002Fcli\u002Fadd-seeder","hu\u002F4.cli\u002F15.add-seeder","i-lucide-sprout",{"title":320,"path":321,"stem":322,"icon":323},"new project","\u002Fhu\u002Fcli\u002Fnew-project","hu\u002F4.cli\u002F2.new-project","i-lucide-folder-plus",{"title":325,"path":326,"stem":327,"icon":328},"new module","\u002Fhu\u002Fcli\u002Fnew-module","hu\u002F4.cli\u002F3.new-module","i-lucide-blocks",{"title":330,"path":331,"stem":332,"icon":333},"add surface","\u002Fhu\u002Fcli\u002Fadd-surface","hu\u002F4.cli\u002F4.add-surface","i-lucide-layers-2",{"title":335,"path":336,"stem":337,"icon":244},"add core","\u002Fhu\u002Fcli\u002Fadd-core","hu\u002F4.cli\u002F5.add-core",{"title":339,"path":340,"stem":341,"icon":342},"add handler","\u002Fhu\u002Fcli\u002Fadd-handler","hu\u002F4.cli\u002F6.add-handler","i-lucide-webhook",{"title":344,"path":345,"stem":346,"icon":347},"new migration","\u002Fhu\u002Fcli\u002Fnew-migration","hu\u002F4.cli\u002F7.new-migration","i-lucide-file-plus",{"title":349,"path":350,"stem":351,"icon":266},"worker generátorok","\u002Fhu\u002Fcli\u002Fworker-generators","hu\u002F4.cli\u002F8.worker-generators",{"title":353,"path":354,"stem":355,"icon":356},"upgrade templates","\u002Fhu\u002Fcli\u002Fupgrade-templates","hu\u002F4.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"title":358,"icon":227,"path":359,"stem":360,"children":361,"page":44},"errs","\u002Fhu\u002Ferrs","hu\u002F5.errs",[362,365,369],{"title":52,"path":363,"stem":364,"icon":227},"\u002Fhu\u002Ferrs\u002Foverview","hu\u002F5.errs\u002F1.overview",{"title":366,"path":367,"stem":368},"API","\u002Fhu\u002Ferrs\u002Fapi","hu\u002F5.errs\u002F2.api",{"title":370,"path":371,"stem":372,"icon":227},"Integráció","\u002Fhu\u002Ferrs\u002Fintegration","hu\u002F5.errs\u002F3.integration",{"title":374,"icon":231,"path":375,"stem":376,"children":377,"page":44},"envconf","\u002Fhu\u002Fenvconf","hu\u002F6.envconf",[378,381],{"title":52,"path":379,"stem":380,"icon":231},"\u002Fhu\u002Fenvconf\u002Foverview","hu\u002F6.envconf\u002F1.overview",{"title":382,"path":383,"stem":384},"Receptek","\u002Fhu\u002Fenvconf\u002Frecipes","hu\u002F6.envconf\u002F2.recipes",{"title":386,"icon":387,"path":388,"stem":389,"children":390,"page":44},"httperr","i-lucide-globe","\u002Fhu\u002Fhttperr","hu\u002F7.httperr",[391,394,399],{"title":52,"path":392,"stem":393,"icon":387},"\u002Fhu\u002Fhttperr\u002Foverview","hu\u002F7.httperr\u002F1.overview",{"title":395,"path":396,"stem":397,"icon":398},"Hibaválaszok","\u002Fhu\u002Fhttperr\u002Ferror-responses","hu\u002F7.httperr\u002F2.error-responses","i-lucide-octagon-alert",{"title":400,"path":401,"stem":402,"icon":403},"Validáció","\u002Fhu\u002Fhttperr\u002Fvalidation","hu\u002F7.httperr\u002F3.validation","i-lucide-badge-check",{"title":405,"icon":406,"path":407,"stem":408,"children":409,"page":44},"dbx","i-lucide-database","\u002Fhu\u002Fdbx","hu\u002F8.dbx",[410,413,417,421,426],{"title":52,"path":411,"stem":412,"icon":406},"\u002Fhu\u002Fdbx\u002Foverview","hu\u002F8.dbx\u002F1.overview",{"title":414,"path":415,"stem":416,"icon":106},"pgx","\u002Fhu\u002Fdbx\u002Fpg","hu\u002F8.dbx\u002F2.pg",{"title":418,"path":419,"stem":420,"icon":47},"bun","\u002Fhu\u002Fdbx\u002Fbunx","hu\u002F8.dbx\u002F3.bunx",{"title":422,"path":423,"stem":424,"icon":425},"Tranzakciók","\u002Fhu\u002Fdbx\u002Ftransactions","hu\u002F8.dbx\u002F4.transactions","i-lucide-git-merge",{"title":427,"path":428,"stem":429,"icon":318},"Seederek","\u002Fhu\u002Fdbx\u002Fseed","hu\u002F8.dbx\u002F5.seed",{"title":431,"icon":55,"path":432,"stem":433,"children":434,"page":44},"paginate","\u002Fhu\u002Fpaginate","hu\u002F9.paginate",[435,438],{"title":52,"path":436,"stem":437,"icon":55},"\u002Fhu\u002Fpaginate\u002Foverview","hu\u002F9.paginate\u002F1.overview",{"title":439,"path":440,"stem":441,"icon":19},"Cursor-lapozás","\u002Fhu\u002Fpaginate\u002Fcursor","hu\u002F9.paginate\u002F2.cursor",{"id":443,"title":238,"body":444,"description":4901,"extension":4902,"links":4903,"meta":4904,"navigation":4905,"path":239,"seo":4906,"stem":240,"__hash__":4907},"docs_hu\u002Fhu\u002F2.concepts\u002F5.design-patterns.md",{"type":445,"value":446,"toc":4862},"minimark",[447,456,463,468,473,500,518,795,854,866,898,902,907,1192,1201,1211,1215,1228,1361,1381,1390,1394,1410,1569,1600,1610,1614,1644,1875,1896,1908,1925,1939,1943,1947,1967,2099,2112,2121,2129,2145,2349,2359,2368,2376,2396,2542,2557,2569,2573,2590,2765,2782,2791,2795,2799,2814,2974,2993,3003,3013,3017,3032,3357,3366,3384,3393,3397,3410,3505,3524,3538,3542,3546,3555,3628,3637,3651,3660,3664,3677,3697,3716,3725,3729,3742,3803,3816,3825,3829,3833,3849,3982,3995,4003,4007,4042,4229,4245,4255,4259,4274,4287,4297,4301,4305,4314,4490,4503,4508,4512,4529,4714,4731,4748,4752,4757,4768,4782,4786,4792,4833,4837,4858],[448,449,450,451,455],"p",{},"Ez az oldal nem új réteg, hanem ",[452,453,454],"strong",{},"térkép a már meglévő kódhoz",": azokat a tervezési mintákat gyűjti egy helyre, amik a kit több csomagjában is visszatérnek. Mindegyik mintánál ugyanaz a három dolog szerepel: mi ez, miért ezt választotta a kit (nem \"mert jó gyakorlat\", hanem a konkrét kényszer, ami idehozta), és hol keresd meg élesben. A minták nem újak vagy gpsystem-specifikusak: a Go-ökoszisztéma és a szélesebb szoftverfejlesztés bevett eszközei; a linkek a kanonikus leírásukra mutatnak.",[448,457,458,459,462],{},"Ha még nem olvastad, előbb az ",[460,461,220],"a",{"href":221}," oldal érdemes: ott van a csomagtérkép és a „centralizál, nem absztrahál\" alapelv, aminek ez az oldal a részletezése.",[464,465,467],"h2",{"id":466},"http-és-api-réteg","HTTP és API-réteg",[469,470,472],"h3",{"id":471},"functional-options","Functional options",[448,474,475,478,479,483,484,487,488,491,492,495,496,499],{},[452,476,477],{},"Mi:"," egy konstruktor ",[480,481,482],"code",{},"...Option","-t fogad, ahol ",[480,485,486],{},"Option"," egy ",[480,489,490],{},"func(*belsőStruct)",", így a ",[480,493,494],{},"New","\u002F",[480,497,498],{},"Run"," szignatúrája stabil marad, miközben tetszőleges számú opcionális paraméter adható át, backward-compatible módon bővíthetően.",[448,501,502,505,506,495,508,495,510,513,514,517],{},[452,503,504],{},"Miért itt:"," a kit ",[480,507,494],{},[480,509,498],{},[480,511,512],{},"Setup"," függvényeinek 5-10 opcionális beállítása van (closer, middleware, validator, exporter...), és ezek variadic pozicionális paraméterként olvashatatlanok, egy ",[480,515,516],{},"Config"," structként pedig minden hívónak az összes mezőt látnia kéne akkor is, ha csak egyet akar módosítani.",[519,520,525],"pre",{"className":521,"code":522,"language":523,"meta":524,"style":524},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F server\u002Fserver.go\ntype Option func(*options)\n\nfunc WithCloser(name string, fn func(context.Context) error) Option {\n    return func(o *options) { o.closers = append(o.closers, app.Closer{Name: name, Fn: fn}) }\n}\nfunc WithMiddleware(mw ...func(http.Handler) http.Handler) Option {\n    return func(o *options) { o.middleware = append(o.middleware, mw...) }\n}\n","go","",[480,526,527,536,556,563,615,698,704,743,790],{"__ignoreMap":524},[528,529,532],"span",{"class":530,"line":531},"line",1,[528,533,535],{"class":534},"sHwdD","\u002F\u002F server\u002Fserver.go\n",[528,537,539,543,547,550,553],{"class":530,"line":538},2,[528,540,542],{"class":541},"sMK4o","type",[528,544,546],{"class":545},"sBMFI"," Option",[528,548,549],{"class":541}," func(*",[528,551,552],{"class":545},"options",[528,554,555],{"class":541},")\n",[528,557,559],{"class":530,"line":558},3,[528,560,562],{"emptyLinePlaceholder":561},true,"\n",[528,564,566,569,573,576,580,584,587,590,593,596,599,602,605,608,610,612],{"class":530,"line":565},4,[528,567,568],{"class":541},"func",[528,570,572],{"class":571},"s2Zo4"," WithCloser",[528,574,575],{"class":541},"(",[528,577,579],{"class":578},"sHdIc","name",[528,581,583],{"class":582},"spNyl"," string",[528,585,586],{"class":541},",",[528,588,589],{"class":578}," fn",[528,591,592],{"class":541}," func(",[528,594,595],{"class":545},"context",[528,597,598],{"class":541},".",[528,600,601],{"class":545},"Context",[528,603,604],{"class":541},")",[528,606,607],{"class":582}," error",[528,609,604],{"class":541},[528,611,546],{"class":545},[528,613,614],{"class":541}," {\n",[528,616,618,622,624,627,630,632,634,637,641,643,646,649,652,654,656,658,661,663,666,668,671,674,677,680,683,685,688,690,692,695],{"class":530,"line":617},5,[528,619,621],{"class":620},"s7zQu","    return",[528,623,592],{"class":541},[528,625,626],{"class":578},"o",[528,628,629],{"class":541}," *",[528,631,552],{"class":545},[528,633,604],{"class":541},[528,635,636],{"class":541}," {",[528,638,640],{"class":639},"sTEyZ"," o",[528,642,598],{"class":541},[528,644,645],{"class":639},"closers ",[528,647,648],{"class":541},"=",[528,650,651],{"class":571}," append",[528,653,575],{"class":541},[528,655,626],{"class":639},[528,657,598],{"class":541},[528,659,660],{"class":639},"closers",[528,662,586],{"class":541},[528,664,665],{"class":545}," app",[528,667,598],{"class":541},[528,669,670],{"class":545},"Closer",[528,672,673],{"class":541},"{",[528,675,676],{"class":639},"Name",[528,678,679],{"class":541},":",[528,681,682],{"class":639}," name",[528,684,586],{"class":541},[528,686,687],{"class":639}," Fn",[528,689,679],{"class":541},[528,691,589],{"class":639},[528,693,694],{"class":541},"})",[528,696,697],{"class":541}," }\n",[528,699,701],{"class":530,"line":700},6,[528,702,703],{"class":541},"}\n",[528,705,707,709,712,714,717,720,723,725,728,730,733,735,737,739,741],{"class":530,"line":706},7,[528,708,568],{"class":541},[528,710,711],{"class":571}," WithMiddleware",[528,713,575],{"class":541},[528,715,716],{"class":578},"mw",[528,718,719],{"class":541}," ...func(",[528,721,722],{"class":545},"http",[528,724,598],{"class":541},[528,726,727],{"class":545},"Handler",[528,729,604],{"class":541},[528,731,732],{"class":545}," http",[528,734,598],{"class":541},[528,736,727],{"class":545},[528,738,604],{"class":541},[528,740,546],{"class":545},[528,742,614],{"class":541},[528,744,746,748,750,752,754,756,758,760,762,764,767,769,771,773,775,777,780,782,785,788],{"class":530,"line":745},8,[528,747,621],{"class":620},[528,749,592],{"class":541},[528,751,626],{"class":578},[528,753,629],{"class":541},[528,755,552],{"class":545},[528,757,604],{"class":541},[528,759,636],{"class":541},[528,761,640],{"class":639},[528,763,598],{"class":541},[528,765,766],{"class":639},"middleware ",[528,768,648],{"class":541},[528,770,651],{"class":571},[528,772,575],{"class":541},[528,774,626],{"class":639},[528,776,598],{"class":541},[528,778,779],{"class":639},"middleware",[528,781,586],{"class":541},[528,783,784],{"class":639}," mw",[528,786,787],{"class":541},"...)",[528,789,697],{"class":541},[528,791,793],{"class":530,"line":792},9,[528,794,703],{"class":541},[448,796,797,505,800,805,806,809,810,809,813,816,817,822,823,826,827,822,832,809,835,838,839,842,843,822,847,809,850,853],{},[452,798,799],{},"Hol:",[460,801,802],{"href":259},[480,803,804],{},"server"," csomagja (",[480,807,808],{},"WithCloser",", ",[480,811,812],{},"WithMiddleware",[480,814,815],{},"WithHTTPServer","), a ",[460,818,819],{"href":189},[480,820,821],{},"telemetry\u002Fotelx"," (",[480,824,825],{},"WithSpanExporter",", nil-toleráns, lásd lent), a ",[460,828,829],{"href":401},[480,830,831],{},"httperr\u002Fvalidate",[480,833,834],{},"WithTagNameFunc",[480,836,837],{},"WithRegister",", közvetlenül a becsomagolt ",[480,840,841],{},"*validator.Validate","-en), a ",[460,844,845],{"href":436},[480,846,431],{},[480,848,849],{},"WithDefaultPerPage",[480,851,852],{},"WithMaxPerPage",").",[448,855,856,859,860,598],{},[452,857,858],{},"Külső forrás:"," ",[460,861,865],{"href":862,"rel":863},"https:\u002F\u002Fdave.cheney.net\u002F2014\u002F10\u002F17\u002Ffunctional-options-for-friendly-apis",[864],"nofollow","Dave Cheney: Functional options for friendly APIs",[867,868,869,870,873,874,877,878,881,882,885,886,889,890,893,894,897],"tip",{},"Egy variáns, ami a kitben is előfordul: az ",[452,871,872],{},"opció maga elnyeli a nil\u002Füres esetet",", hogy a hívó feltétel nélkül adhassa át. Az ",[480,875,876],{},"otelx.WithSpanExporter(exp)"," egyszerűen kihagyja ",[480,879,880],{},"exp == nil","-nél, így a ",[480,883,884],{},"telemetry.Setup"," a ",[480,887,888],{},"sentryHandle.SpanExporter()","-t (ami ",[480,891,892],{},"nil",", ha a Sentry ki van kapcsolva) mindig átadhatja, ",[480,895,896],{},"if","-ág nélkül.",[469,899,901],{"id":900},"middleware-lánc-rögzített-sorrenddel","Middleware lánc rögzített sorrenddel",[448,903,904,906],{},[452,905,477],{}," a keretrendszer middleware-sorozata (recovery, telemetria, CORS, ...) fix, dokumentált sorrendben épül fel, mielőtt a felhasználói middleware vagy a route-ok jönnének, a sorrend maga a viselkedés (pl. a recovery mindig legkívül van, hogy minden lejjebbi middleware panicját is elkapja).",[519,908,910],{"className":521,"code":909,"language":523,"meta":524,"style":524},"\u002F\u002F server\u002Fserver.go: newMux\nmux.Use(recoverMiddleware)\nif !o.withoutTelemetry {\n    mux.Use(otelhttp.NewMiddleware(\"\"))\n}\nmux.Use(func(next http.Handler) http.Handler {\n    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        next.ServeHTTP(w, r.WithContext(sentryx.WithRequestHub(r.Context())))\n    })\n})\nmux.Use(cors.Handler(cors.Options{ \u002F* ... *\u002F }))\nfor _, mw := range o.middleware {\n    mux.Use(mw)\n}\n",[480,911,912,917,934,951,978,982,1012,1053,1098,1103,1109,1144,1172,1187],{"__ignoreMap":524},[528,913,914],{"class":530,"line":531},[528,915,916],{"class":534},"\u002F\u002F server\u002Fserver.go: newMux\n",[528,918,919,922,924,927,929,932],{"class":530,"line":538},[528,920,921],{"class":639},"mux",[528,923,598],{"class":541},[528,925,926],{"class":571},"Use",[528,928,575],{"class":541},[528,930,931],{"class":639},"recoverMiddleware",[528,933,555],{"class":541},[528,935,936,938,941,943,945,948],{"class":530,"line":558},[528,937,896],{"class":620},[528,939,940],{"class":541}," !",[528,942,626],{"class":639},[528,944,598],{"class":541},[528,946,947],{"class":639},"withoutTelemetry ",[528,949,950],{"class":541},"{\n",[528,952,953,956,958,960,962,965,967,970,972,975],{"class":530,"line":565},[528,954,955],{"class":639},"    mux",[528,957,598],{"class":541},[528,959,926],{"class":571},[528,961,575],{"class":541},[528,963,964],{"class":639},"otelhttp",[528,966,598],{"class":541},[528,968,969],{"class":571},"NewMiddleware",[528,971,575],{"class":541},[528,973,974],{"class":541},"\"\"",[528,976,977],{"class":541},"))\n",[528,979,980],{"class":530,"line":617},[528,981,703],{"class":541},[528,983,984,986,988,990,993,996,998,1000,1002,1004,1006,1008,1010],{"class":530,"line":700},[528,985,921],{"class":639},[528,987,598],{"class":541},[528,989,926],{"class":571},[528,991,992],{"class":541},"(func(",[528,994,995],{"class":578},"next",[528,997,732],{"class":545},[528,999,598],{"class":541},[528,1001,727],{"class":545},[528,1003,604],{"class":541},[528,1005,732],{"class":545},[528,1007,598],{"class":541},[528,1009,727],{"class":545},[528,1011,614],{"class":541},[528,1013,1014,1016,1018,1020,1023,1025,1028,1030,1032,1035,1037,1040,1042,1044,1046,1049,1051],{"class":530,"line":706},[528,1015,621],{"class":620},[528,1017,732],{"class":639},[528,1019,598],{"class":541},[528,1021,1022],{"class":571},"HandlerFunc",[528,1024,992],{"class":541},[528,1026,1027],{"class":578},"w",[528,1029,732],{"class":545},[528,1031,598],{"class":541},[528,1033,1034],{"class":545},"ResponseWriter",[528,1036,586],{"class":541},[528,1038,1039],{"class":578}," r",[528,1041,629],{"class":541},[528,1043,722],{"class":545},[528,1045,598],{"class":541},[528,1047,1048],{"class":545},"Request",[528,1050,604],{"class":541},[528,1052,614],{"class":541},[528,1054,1055,1058,1060,1063,1065,1067,1069,1071,1073,1076,1078,1081,1083,1086,1088,1091,1093,1095],{"class":530,"line":745},[528,1056,1057],{"class":639},"        next",[528,1059,598],{"class":541},[528,1061,1062],{"class":571},"ServeHTTP",[528,1064,575],{"class":541},[528,1066,1027],{"class":639},[528,1068,586],{"class":541},[528,1070,1039],{"class":639},[528,1072,598],{"class":541},[528,1074,1075],{"class":571},"WithContext",[528,1077,575],{"class":541},[528,1079,1080],{"class":639},"sentryx",[528,1082,598],{"class":541},[528,1084,1085],{"class":571},"WithRequestHub",[528,1087,575],{"class":541},[528,1089,1090],{"class":639},"r",[528,1092,598],{"class":541},[528,1094,601],{"class":571},[528,1096,1097],{"class":541},"())))\n",[528,1099,1100],{"class":530,"line":792},[528,1101,1102],{"class":541},"    })\n",[528,1104,1106],{"class":530,"line":1105},10,[528,1107,1108],{"class":541},"})\n",[528,1110,1112,1114,1116,1118,1120,1123,1125,1127,1129,1131,1133,1136,1138,1141],{"class":530,"line":1111},11,[528,1113,921],{"class":639},[528,1115,598],{"class":541},[528,1117,926],{"class":571},[528,1119,575],{"class":541},[528,1121,1122],{"class":639},"cors",[528,1124,598],{"class":541},[528,1126,727],{"class":571},[528,1128,575],{"class":541},[528,1130,1122],{"class":545},[528,1132,598],{"class":541},[528,1134,1135],{"class":545},"Options",[528,1137,673],{"class":541},[528,1139,1140],{"class":534}," \u002F* ... *\u002F",[528,1142,1143],{"class":541}," }))\n",[528,1145,1147,1150,1153,1155,1158,1161,1164,1166,1168,1170],{"class":530,"line":1146},12,[528,1148,1149],{"class":620},"for",[528,1151,1152],{"class":639}," _",[528,1154,586],{"class":541},[528,1156,1157],{"class":639}," mw ",[528,1159,1160],{"class":541},":=",[528,1162,1163],{"class":620}," range",[528,1165,640],{"class":639},[528,1167,598],{"class":541},[528,1169,766],{"class":639},[528,1171,950],{"class":541},[528,1173,1175,1177,1179,1181,1183,1185],{"class":530,"line":1174},13,[528,1176,955],{"class":639},[528,1178,598],{"class":541},[528,1180,926],{"class":571},[528,1182,575],{"class":541},[528,1184,716],{"class":639},[528,1186,555],{"class":541},[528,1188,1190],{"class":530,"line":1189},14,[528,1191,703],{"class":541},[448,1193,1194,505,1196,1200],{},[452,1195,799],{},[460,1197,1198],{"href":259},[480,1199,804],{}," csomagja: recover → otel → sentry-hub → (dev logger) → cors → felhasználói middleware.",[448,1202,1203,1205,1206,598],{},[452,1204,858],{}," a minta maga framework-független; a chi middleware-modellje: ",[460,1207,1210],{"href":1208,"rel":1209},"https:\u002F\u002Fgithub.com\u002Fgo-chi\u002Fchi#middleware-handlers",[864],"go-chi\u002Fchi",[469,1212,1214],{"id":1213},"generikus-middleware-adapter","Generikus middleware-adapter",[448,1216,1217,1219,1220,1223,1224,1227],{},[452,1218,477],{}," Go generikusokkal írt middleware-factory, ami egy ",[480,1221,1222],{},"~func(...)"," alakú típuscsaláddal paraméterezett, így ugyanaz a kód minden oapi-codegen által generált ",[480,1225,1226],{},"StrictHandlerFunc","-hoz illeszkedik anélkül, hogy a konkrét generált típusra hivatkozna.",[519,1229,1231],{"className":521,"code":1230,"language":523,"meta":524,"style":524},"\u002F\u002F server\u002Fstrict.go\nfunc StrictValidator[H ~func(ctx context.Context, w http.ResponseWriter, r *http.Request, request any) (any, error)](\n    v *validate.Validator,\n) func(f H, operationID string) H {\n    \u002F\u002F ...\n}\n",[480,1232,1233,1238,1309,1327,1352,1357],{"__ignoreMap":524},[528,1234,1235],{"class":530,"line":531},[528,1236,1237],{"class":534},"\u002F\u002F server\u002Fstrict.go\n",[528,1239,1240,1242,1245,1248,1251,1254,1257,1260,1262,1264,1266,1269,1271,1273,1275,1277,1279,1281,1283,1285,1287,1289,1292,1295,1297,1299,1302,1304,1306],{"class":530,"line":538},[528,1241,568],{"class":541},[528,1243,1244],{"class":571}," StrictValidator",[528,1246,1247],{"class":541},"[",[528,1249,1250],{"class":578},"H",[528,1252,1253],{"class":541}," ~func(",[528,1255,1256],{"class":578},"ctx",[528,1258,1259],{"class":545}," context",[528,1261,598],{"class":541},[528,1263,601],{"class":545},[528,1265,586],{"class":541},[528,1267,1268],{"class":578}," w",[528,1270,732],{"class":545},[528,1272,598],{"class":541},[528,1274,1034],{"class":545},[528,1276,586],{"class":541},[528,1278,1039],{"class":578},[528,1280,629],{"class":541},[528,1282,722],{"class":545},[528,1284,598],{"class":541},[528,1286,1048],{"class":545},[528,1288,586],{"class":541},[528,1290,1291],{"class":578}," request",[528,1293,1294],{"class":545}," any",[528,1296,604],{"class":541},[528,1298,822],{"class":541},[528,1300,1301],{"class":545},"any",[528,1303,586],{"class":541},[528,1305,607],{"class":582},[528,1307,1308],{"class":541},")](\n",[528,1310,1311,1314,1316,1319,1321,1324],{"class":530,"line":558},[528,1312,1313],{"class":578},"    v",[528,1315,629],{"class":541},[528,1317,1318],{"class":545},"validate",[528,1320,598],{"class":541},[528,1322,1323],{"class":545},"Validator",[528,1325,1326],{"class":541},",\n",[528,1328,1329,1331,1333,1336,1339,1341,1344,1346,1348,1350],{"class":530,"line":565},[528,1330,604],{"class":541},[528,1332,592],{"class":541},[528,1334,1335],{"class":578},"f",[528,1337,1338],{"class":545}," H",[528,1340,586],{"class":541},[528,1342,1343],{"class":578}," operationID",[528,1345,583],{"class":582},[528,1347,604],{"class":541},[528,1349,1338],{"class":545},[528,1351,614],{"class":541},[528,1353,1354],{"class":530,"line":617},[528,1355,1356],{"class":534},"    \u002F\u002F ...\n",[528,1358,1359],{"class":530,"line":700},[528,1360,703],{"class":541},[448,1362,1363,859,1365,809,1370,809,1375,1380],{},[452,1364,799],{},[460,1366,1367],{"href":259},[480,1368,1369],{},"server.StrictValidator[H]",[460,1371,1372],{"href":99},[480,1373,1374],{},"policy.Enforcer[H]",[460,1376,1377],{"href":89},[480,1378,1379],{},"auth.TokenIssuer[C jwt.Claims]",", mindhárom ugyanazt a mintát alkalmazza: a konkrét (generált vagy hívó-definiált) típus a generikus paraméterben marad, a kit kódja pedig egyszer van megírva.",[448,1382,1383,859,1385,598],{},[452,1384,858],{},[460,1386,1389],{"href":1387,"rel":1388},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Ftutorial\u002Fgenerics",[864],"Go generikusok: hivatalos tutorial",[469,1391,1393],{"id":1392},"context-injektált-függőség","Context-injektált függőség",[448,1395,1396,1398,1399,1402,1403,495,1406,1409],{},[452,1397,477],{}," egy érték (validator, identity, tranzakció) egy ",[452,1400,1401],{},"unexported struct kulcsú"," context-be kerül egy ",[480,1404,1405],{},"With*",[480,1407,1408],{},"*FromContext"," függvénypárral, így a middleware és a lentebbi kód között érték adódik át anélkül, hogy globális változó vagy a szignatúrák módosítása kellene, és a kulcs csomagon kívülről nem ütközhet.",[519,1411,1413],{"className":521,"code":1412,"language":523,"meta":524,"style":524},"\u002F\u002F auth\u002Frbac\u002Fidentity.go\ntype identityKey struct{}\n\nfunc WithIdentity(ctx context.Context, id *Identity) context.Context {\n    return context.WithValue(ctx, identityKey{}, id)\n}\nfunc FromContext(ctx context.Context) *Identity {\n    id, _ := ctx.Value(identityKey{}).(*Identity)\n    return id\n}\n",[480,1414,1415,1420,1430,1434,1471,1497,1501,1526,1558,1565],{"__ignoreMap":524},[528,1416,1417],{"class":530,"line":531},[528,1418,1419],{"class":534},"\u002F\u002F auth\u002Frbac\u002Fidentity.go\n",[528,1421,1422,1424,1427],{"class":530,"line":538},[528,1423,542],{"class":541},[528,1425,1426],{"class":545}," identityKey",[528,1428,1429],{"class":541}," struct{}\n",[528,1431,1432],{"class":530,"line":558},[528,1433,562],{"emptyLinePlaceholder":561},[528,1435,1436,1438,1441,1443,1445,1447,1449,1451,1453,1456,1458,1461,1463,1465,1467,1469],{"class":530,"line":565},[528,1437,568],{"class":541},[528,1439,1440],{"class":571}," WithIdentity",[528,1442,575],{"class":541},[528,1444,1256],{"class":578},[528,1446,1259],{"class":545},[528,1448,598],{"class":541},[528,1450,601],{"class":545},[528,1452,586],{"class":541},[528,1454,1455],{"class":578}," id",[528,1457,629],{"class":541},[528,1459,1460],{"class":545},"Identity",[528,1462,604],{"class":541},[528,1464,1259],{"class":545},[528,1466,598],{"class":541},[528,1468,601],{"class":545},[528,1470,614],{"class":541},[528,1472,1473,1475,1477,1479,1482,1484,1486,1488,1490,1493,1495],{"class":530,"line":617},[528,1474,621],{"class":620},[528,1476,1259],{"class":639},[528,1478,598],{"class":541},[528,1480,1481],{"class":571},"WithValue",[528,1483,575],{"class":541},[528,1485,1256],{"class":639},[528,1487,586],{"class":541},[528,1489,1426],{"class":545},[528,1491,1492],{"class":541},"{},",[528,1494,1455],{"class":639},[528,1496,555],{"class":541},[528,1498,1499],{"class":530,"line":700},[528,1500,703],{"class":541},[528,1502,1503,1505,1508,1510,1512,1514,1516,1518,1520,1522,1524],{"class":530,"line":706},[528,1504,568],{"class":541},[528,1506,1507],{"class":571}," FromContext",[528,1509,575],{"class":541},[528,1511,1256],{"class":578},[528,1513,1259],{"class":545},[528,1515,598],{"class":541},[528,1517,601],{"class":545},[528,1519,604],{"class":541},[528,1521,629],{"class":541},[528,1523,1460],{"class":545},[528,1525,614],{"class":541},[528,1527,1528,1531,1533,1536,1538,1541,1543,1546,1548,1551,1554,1556],{"class":530,"line":745},[528,1529,1530],{"class":639},"    id",[528,1532,586],{"class":541},[528,1534,1535],{"class":639}," _ ",[528,1537,1160],{"class":541},[528,1539,1540],{"class":639}," ctx",[528,1542,598],{"class":541},[528,1544,1545],{"class":571},"Value",[528,1547,575],{"class":541},[528,1549,1550],{"class":545},"identityKey",[528,1552,1553],{"class":541},"{}).(*",[528,1555,1460],{"class":545},[528,1557,555],{"class":541},[528,1559,1560,1562],{"class":530,"line":792},[528,1561,621],{"class":620},[528,1563,1564],{"class":639}," id\n",[528,1566,1567],{"class":530,"line":1105},[528,1568,703],{"class":541},[448,1570,1571,859,1573,1581,1582,1584,1585,1588,1589,1592,1593,1596,1597,1599],{},[452,1572,799],{},[460,1574,1575,495,1578],{"href":94},[480,1576,1577],{},"rbac.WithIdentity",[480,1579,1580],{},"FromContext",", a kit ",[480,1583,804],{},"-jének validator-injektálása (",[480,1586,1587],{},"server\u002Fstrict.go","), ",[480,1590,1591],{},"dbx\u002Fpg"," és ",[480,1594,1595],{},"dbx\u002Fbunx"," tranzakció-hordozás: lásd ",[460,1598,422],{"href":423}," alább.",[448,1601,1602,859,1604,1609],{},[452,1603,858],{},[460,1605,1608],{"href":1606,"rel":1607},"https:\u002F\u002Fgo.dev\u002Fblog\u002Fcontext-and-structs",[864],"Go blog: Context and structs"," (a context-kulcsok helyes használatáról).",[469,1611,1613],{"id":1612},"service-layer","Service layer",[448,1615,1616,1618,1619,1622,1623,1626,1627,1630,1631,1635,1636,1639,1640,1643],{},[452,1617,477],{}," modulonként ",[452,1620,1621],{},"egyetlen",", generált ",[480,1624,1625],{},"core\u002F"," csomag tartja a közös üzleti logikát (sima structok metódusokkal, egyszer leírt szabályok, sentinel-hibák), és minden surface ",[480,1628,1629],{},"service\u002F"," csomagja egy vékony, célközönség-specifikus varrat fölötte: tranzakcióhatárt nyit, a közös szabályokhoz a core-ba delegál, tranzakción belül eventet dob. Nincs külön \"domain model\" réteg a DDD értelmében; a kit a Fowler-féle ",[1632,1633,1634],"em",{},"Service Layer"," \u002F ",[1632,1637,1638],{},"Transaction Script"," mintát választja a teljes hexagonális\u002FDDD-rétegzés helyett. A ",[480,1641,1642],{},"core"," nem DDD domain layer: nincsenek aggregátumok, value objectek és repository-absztrakciós ceremónia, csak a szabály, egy helyen.",[519,1645,1647],{"className":521,"code":1646,"language":523,"meta":524,"style":524},"\u002F\u002F internal\u002Fmodules\u002F\u003Cmodul>\u002Fsurfaces\u002F\u003Csurface>\u002Fservice\u002Fservice.go: a vékony seam\ntype Service struct{ core *core.Service }\n\nfunc (s *Service) Submit(ctx context.Context, req Request) (Ack, error) {\n    return s.core.Submit(ctx, req) \u002F\u002F a közös szabály a modul core-jában él\n}\n\n\u002F\u002F internal\u002Fmodules\u002F\u003Cmodul>\u002Fcore\u002F: a szabály, egyszer leírva\nfunc (c *Service) Submit(ctx context.Context, req Request) (Ack, error) {\n    return c.tx.WithinTransaction(ctx, func(ctx context.Context) error {\n        \u002F\u002F üzleti szabály + repo-hívás + event dispatch, egy helyen\n    })\n}\n",[480,1648,1649,1654,1679,1683,1734,1763,1767,1771,1776,1823,1862,1867,1871],{"__ignoreMap":524},[528,1650,1651],{"class":530,"line":531},[528,1652,1653],{"class":534},"\u002F\u002F internal\u002Fmodules\u002F\u003Cmodul>\u002Fsurfaces\u002F\u003Csurface>\u002Fservice\u002Fservice.go: a vékony seam\n",[528,1655,1656,1658,1661,1664,1667,1670,1672,1674,1677],{"class":530,"line":538},[528,1657,542],{"class":541},[528,1659,1660],{"class":545}," Service",[528,1662,1663],{"class":541}," struct{",[528,1665,1666],{"class":639}," core ",[528,1668,1669],{"class":541},"*",[528,1671,1642],{"class":545},[528,1673,598],{"class":541},[528,1675,1676],{"class":545},"Service",[528,1678,697],{"class":541},[528,1680,1681],{"class":530,"line":558},[528,1682,562],{"emptyLinePlaceholder":561},[528,1684,1685,1687,1689,1692,1694,1696,1698,1701,1703,1705,1707,1709,1711,1713,1716,1719,1721,1723,1726,1728,1730,1732],{"class":530,"line":565},[528,1686,568],{"class":541},[528,1688,822],{"class":541},[528,1690,1691],{"class":578},"s ",[528,1693,1669],{"class":541},[528,1695,1676],{"class":545},[528,1697,604],{"class":541},[528,1699,1700],{"class":571}," Submit",[528,1702,575],{"class":541},[528,1704,1256],{"class":578},[528,1706,1259],{"class":545},[528,1708,598],{"class":541},[528,1710,601],{"class":545},[528,1712,586],{"class":541},[528,1714,1715],{"class":578}," req",[528,1717,1718],{"class":545}," Request",[528,1720,604],{"class":541},[528,1722,822],{"class":541},[528,1724,1725],{"class":545},"Ack",[528,1727,586],{"class":541},[528,1729,607],{"class":582},[528,1731,604],{"class":541},[528,1733,614],{"class":541},[528,1735,1736,1738,1741,1743,1745,1747,1750,1752,1754,1756,1758,1760],{"class":530,"line":617},[528,1737,621],{"class":620},[528,1739,1740],{"class":639}," s",[528,1742,598],{"class":541},[528,1744,1642],{"class":639},[528,1746,598],{"class":541},[528,1748,1749],{"class":571},"Submit",[528,1751,575],{"class":541},[528,1753,1256],{"class":639},[528,1755,586],{"class":541},[528,1757,1715],{"class":639},[528,1759,604],{"class":541},[528,1761,1762],{"class":534}," \u002F\u002F a közös szabály a modul core-jában él\n",[528,1764,1765],{"class":530,"line":700},[528,1766,703],{"class":541},[528,1768,1769],{"class":530,"line":706},[528,1770,562],{"emptyLinePlaceholder":561},[528,1772,1773],{"class":530,"line":745},[528,1774,1775],{"class":534},"\u002F\u002F internal\u002Fmodules\u002F\u003Cmodul>\u002Fcore\u002F: a szabály, egyszer leírva\n",[528,1777,1778,1780,1782,1785,1787,1789,1791,1793,1795,1797,1799,1801,1803,1805,1807,1809,1811,1813,1815,1817,1819,1821],{"class":530,"line":792},[528,1779,568],{"class":541},[528,1781,822],{"class":541},[528,1783,1784],{"class":578},"c ",[528,1786,1669],{"class":541},[528,1788,1676],{"class":545},[528,1790,604],{"class":541},[528,1792,1700],{"class":571},[528,1794,575],{"class":541},[528,1796,1256],{"class":578},[528,1798,1259],{"class":545},[528,1800,598],{"class":541},[528,1802,601],{"class":545},[528,1804,586],{"class":541},[528,1806,1715],{"class":578},[528,1808,1718],{"class":545},[528,1810,604],{"class":541},[528,1812,822],{"class":541},[528,1814,1725],{"class":545},[528,1816,586],{"class":541},[528,1818,607],{"class":582},[528,1820,604],{"class":541},[528,1822,614],{"class":541},[528,1824,1825,1827,1830,1832,1835,1837,1840,1842,1844,1846,1848,1850,1852,1854,1856,1858,1860],{"class":530,"line":1105},[528,1826,621],{"class":620},[528,1828,1829],{"class":639}," c",[528,1831,598],{"class":541},[528,1833,1834],{"class":639},"tx",[528,1836,598],{"class":541},[528,1838,1839],{"class":571},"WithinTransaction",[528,1841,575],{"class":541},[528,1843,1256],{"class":639},[528,1845,586],{"class":541},[528,1847,592],{"class":541},[528,1849,1256],{"class":578},[528,1851,1259],{"class":545},[528,1853,598],{"class":541},[528,1855,601],{"class":545},[528,1857,604],{"class":541},[528,1859,607],{"class":582},[528,1861,614],{"class":541},[528,1863,1864],{"class":530,"line":1111},[528,1865,1866],{"class":534},"        \u002F\u002F üzleti szabály + repo-hívás + event dispatch, egy helyen\n",[528,1868,1869],{"class":530,"line":1146},[528,1870,1102],{"class":541},[528,1872,1873],{"class":530,"line":1174},[528,1874,703],{"class":541},[448,1876,1877,1879,1880,1884,1885,1887,1888,1891,1892,1895],{},[452,1878,504],{}," a gpsystemmel épített projektek kis-közepes CRUD-közeli backendek, surface-enként egy handler-lánccal. Egy teljes DDD-rétegzés extra package-et, extra interfészt, extra konstruktort jelentene minden generált surface-hez, invariáns-gazdag aggregátumok nélkül ez tiszta ceremónia, és ellentmond a kit ",[460,1881,1883],{"href":1882},"\u002Fhu\u002Fconcepts\u002Farchitecture#centraliz%C3%A1l-nem-absztrah%C3%A1l","\"centralizál, ne absztrahálj\""," elvének. Amit a több belépési pont ténylegesen igényel (több surface osztozik a szabályokon), azt a generált ",[480,1886,1642],{}," adja; a surface ",[480,1889,1890],{},"service"," DDD-fogalmakkal az ",[1632,1893,1894],{},"application service"," szerepét tölti be.",[448,1897,1898,1901,1902,1904,1905,1907],{},[452,1899,1900],{},"Növekedési út:"," ha nőnek az invariánsok, ne vezess be új réteget: növessz sima structokat metódusokkal a modul ",[480,1903,1625],{}," csomagjában. Ami csak egy surface-t érint, az maradhat a surface ",[480,1906,1890],{},"-ében; amint egy második surface-nek is kell, költözik a core-ba. Ha egy projekt ennél tényleg többet igényel (valódi aggregátumok, több bounded context, kontextusonként külön modell), azzal kinőtted a gpsystemet: az egy kézzel rétegzett architektúra terepe, nem a generátoré.",[448,1909,1910,859,1912,1916,1917,1920,1921,1924],{},[452,1911,799],{},[460,1913,1914],{"href":331},[480,1915,330],{}," generálja a seamet és az első surface mellé a core-t; a rétegszabály (",[480,1918,1919],{},"http → service → core → repository",") a ",[460,1922,35],{"href":1923},"\u002Fhu\u002Fgetting-started\u002Fproject-structure#r%C3%A9tegszab%C3%A1lyok"," oldalon.",[448,1926,1927,859,1929,809,1934,598],{},[452,1928,858],{},[460,1930,1933],{"href":1931,"rel":1932},"https:\u002F\u002Fmartinfowler.com\u002FeaaCatalog\u002FserviceLayer.html",[864],"martinfowler.com: Service Layer",[460,1935,1938],{"href":1936,"rel":1937},"https:\u002F\u002Fmartinfowler.com\u002FeaaCatalog\u002FtransactionScript.html",[864],"martinfowler.com: Transaction Script",[464,1940,1942],{"id":1941},"hibakezelés","Hibakezelés",[469,1944,1946],{"id":1945},"rfc-9457-problem-details","RFC 9457 Problem Details",[448,1948,1949,1951,1952,809,1954,809,1957,809,1960,809,1963,1966],{},[452,1950,477],{}," minden HTTP-hibaválasz egy szabványosított JSON-objektum (",[480,1953,542],{},[480,1955,1956],{},"title",[480,1958,1959],{},"status",[480,1961,1962],{},"detail",[480,1964,1965],{},"instance","), amit a kliens gépi kódból egységesen tud feldolgozni, ahelyett, hogy minden endpoint saját hibaalakot találna ki.",[519,1968,1970],{"className":521,"code":1969,"language":523,"meta":524,"style":524},"\u002F\u002F httperr\u002Fproblem.go\ntype Problem struct {\n    Type   string `json:\"type\"`\n    Title  string `json:\"title\"`\n    Status int    `json:\"status\"`\n    Detail string `json:\"detail,omitempty\"`\n    Code   string `json:\"code,omitempty\"`\n}\nfunc (p *Problem) Error() string { \u002F* ... *\u002F }\n",[480,1971,1972,1977,1989,2007,2021,2037,2051,2065,2069],{"__ignoreMap":524},[528,1973,1974],{"class":530,"line":531},[528,1975,1976],{"class":534},"\u002F\u002F httperr\u002Fproblem.go\n",[528,1978,1979,1981,1984,1987],{"class":530,"line":538},[528,1980,542],{"class":541},[528,1982,1983],{"class":545}," Problem",[528,1985,1986],{"class":541}," struct",[528,1988,614],{"class":541},[528,1990,1991,1994,1997,2000,2004],{"class":530,"line":558},[528,1992,1993],{"class":639},"    Type   ",[528,1995,1996],{"class":582},"string",[528,1998,1999],{"class":541}," `",[528,2001,2003],{"class":2002},"sfazB","json:\"type\"",[528,2005,2006],{"class":541},"`\n",[528,2008,2009,2012,2014,2016,2019],{"class":530,"line":565},[528,2010,2011],{"class":639},"    Title  ",[528,2013,1996],{"class":582},[528,2015,1999],{"class":541},[528,2017,2018],{"class":2002},"json:\"title\"",[528,2020,2006],{"class":541},[528,2022,2023,2026,2029,2032,2035],{"class":530,"line":617},[528,2024,2025],{"class":639},"    Status ",[528,2027,2028],{"class":582},"int",[528,2030,2031],{"class":541},"    `",[528,2033,2034],{"class":2002},"json:\"status\"",[528,2036,2006],{"class":541},[528,2038,2039,2042,2044,2046,2049],{"class":530,"line":700},[528,2040,2041],{"class":639},"    Detail ",[528,2043,1996],{"class":582},[528,2045,1999],{"class":541},[528,2047,2048],{"class":2002},"json:\"detail,omitempty\"",[528,2050,2006],{"class":541},[528,2052,2053,2056,2058,2060,2063],{"class":530,"line":706},[528,2054,2055],{"class":639},"    Code   ",[528,2057,1996],{"class":582},[528,2059,1999],{"class":541},[528,2061,2062],{"class":2002},"json:\"code,omitempty\"",[528,2064,2006],{"class":541},[528,2066,2067],{"class":530,"line":745},[528,2068,703],{"class":541},[528,2070,2071,2073,2075,2078,2080,2083,2085,2088,2091,2093,2095,2097],{"class":530,"line":792},[528,2072,568],{"class":541},[528,2074,822],{"class":541},[528,2076,2077],{"class":578},"p ",[528,2079,1669],{"class":541},[528,2081,2082],{"class":545},"Problem",[528,2084,604],{"class":541},[528,2086,2087],{"class":571}," Error",[528,2089,2090],{"class":541},"()",[528,2092,583],{"class":582},[528,2094,636],{"class":541},[528,2096,1140],{"class":534},[528,2098,697],{"class":541},[448,2100,2101,859,2103,2108,2109,598],{},[452,2102,799],{},[460,2104,2105],{"href":396},[480,2106,2107],{},"httperr.Problem",", a teljes ",[460,2110,2111],{"href":225},"hibamodell",[448,2113,2114,859,2116,598],{},[452,2115,858],{},[460,2117,2120],{"href":2118,"rel":2119},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc9457",[864],"RFC 9457: Problem Details for HTTP APIs",[469,2122,2124,2125,2128],{"id":2123},"kaszkádoló-errorsas-mapping","Kaszkádoló ",[480,2126,2127],{},"errors.As"," mapping",[448,2130,2131,2133,2134,2136,2137,2140,2141,2144],{},[452,2132,477],{}," egy típus-switch, ami ",[480,2135,2127],{},"-sel sorban megpróbál egyre általánosabb hibatípusokra illeszteni (saját ",[480,2138,2139],{},"*Problem"," → egy kérés-dekódolási hiba → generikus ",[480,2142,2143],{},"*errs.Error"," → minden más), és az első találatnál dönt a HTTP-válaszról.",[519,2146,2148],{"className":521,"code":2147,"language":523,"meta":524,"style":524},"\u002F\u002F httperr\u002Fhandler.go: mapProblem\nvar p *Problem\nif errors.As(err, &p) { cp := *p; return &cp }\nvar de *DecodeError\nif errors.As(err, &de) { return BadRequest(de.Error()) }\nvar ee *errs.Error\nif errors.As(err, &ee) { \u002F* status\u002Fcode kinyerése *\u002F }\nreturn New(http.StatusInternalServerError, \"\", detail)\n",[480,2149,2150,2155,2168,2218,2230,2274,2290,2320],{"__ignoreMap":524},[528,2151,2152],{"class":530,"line":531},[528,2153,2154],{"class":534},"\u002F\u002F httperr\u002Fhandler.go: mapProblem\n",[528,2156,2157,2160,2163,2165],{"class":530,"line":538},[528,2158,2159],{"class":541},"var",[528,2161,2162],{"class":639}," p ",[528,2164,1669],{"class":541},[528,2166,2167],{"class":545},"Problem\n",[528,2169,2170,2172,2175,2177,2180,2182,2185,2187,2190,2192,2194,2196,2199,2201,2203,2205,2208,2211,2213,2216],{"class":530,"line":558},[528,2171,896],{"class":620},[528,2173,2174],{"class":639}," errors",[528,2176,598],{"class":541},[528,2178,2179],{"class":571},"As",[528,2181,575],{"class":541},[528,2183,2184],{"class":639},"err",[528,2186,586],{"class":541},[528,2188,2189],{"class":541}," &",[528,2191,448],{"class":639},[528,2193,604],{"class":541},[528,2195,636],{"class":541},[528,2197,2198],{"class":639}," cp ",[528,2200,1160],{"class":541},[528,2202,629],{"class":541},[528,2204,448],{"class":639},[528,2206,2207],{"class":541},";",[528,2209,2210],{"class":620}," return",[528,2212,2189],{"class":541},[528,2214,2215],{"class":639},"cp ",[528,2217,703],{"class":541},[528,2219,2220,2222,2225,2227],{"class":530,"line":565},[528,2221,2159],{"class":541},[528,2223,2224],{"class":639}," de ",[528,2226,1669],{"class":541},[528,2228,2229],{"class":545},"DecodeError\n",[528,2231,2232,2234,2236,2238,2240,2242,2244,2246,2248,2251,2253,2255,2257,2260,2262,2264,2266,2269,2272],{"class":530,"line":617},[528,2233,896],{"class":620},[528,2235,2174],{"class":639},[528,2237,598],{"class":541},[528,2239,2179],{"class":571},[528,2241,575],{"class":541},[528,2243,2184],{"class":639},[528,2245,586],{"class":541},[528,2247,2189],{"class":541},[528,2249,2250],{"class":639},"de",[528,2252,604],{"class":541},[528,2254,636],{"class":541},[528,2256,2210],{"class":620},[528,2258,2259],{"class":571}," BadRequest",[528,2261,575],{"class":541},[528,2263,2250],{"class":639},[528,2265,598],{"class":541},[528,2267,2268],{"class":571},"Error",[528,2270,2271],{"class":541},"())",[528,2273,697],{"class":541},[528,2275,2276,2278,2281,2283,2285,2287],{"class":530,"line":700},[528,2277,2159],{"class":541},[528,2279,2280],{"class":639}," ee ",[528,2282,1669],{"class":541},[528,2284,358],{"class":545},[528,2286,598],{"class":541},[528,2288,2289],{"class":545},"Error\n",[528,2291,2292,2294,2296,2298,2300,2302,2304,2306,2308,2311,2313,2315,2318],{"class":530,"line":706},[528,2293,896],{"class":620},[528,2295,2174],{"class":639},[528,2297,598],{"class":541},[528,2299,2179],{"class":571},[528,2301,575],{"class":541},[528,2303,2184],{"class":639},[528,2305,586],{"class":541},[528,2307,2189],{"class":541},[528,2309,2310],{"class":639},"ee",[528,2312,604],{"class":541},[528,2314,636],{"class":541},[528,2316,2317],{"class":534}," \u002F* status\u002Fcode kinyerése *\u002F",[528,2319,697],{"class":541},[528,2321,2322,2325,2328,2330,2332,2334,2337,2339,2342,2344,2347],{"class":530,"line":745},[528,2323,2324],{"class":620},"return",[528,2326,2327],{"class":571}," New",[528,2329,575],{"class":541},[528,2331,722],{"class":639},[528,2333,598],{"class":541},[528,2335,2336],{"class":639},"StatusInternalServerError",[528,2338,586],{"class":541},[528,2340,2341],{"class":541}," \"\"",[528,2343,586],{"class":541},[528,2345,2346],{"class":639}," detail",[528,2348,555],{"class":541},[448,2350,2351,859,2353,2358],{},[452,2352,799],{},[460,2354,2355],{"href":396},[480,2356,2357],{},"httperr.mapProblem",": egyetlen mapping-hely, a generált szerver hibahookjaiba kötve.",[448,2360,2361,859,2363,598],{},[452,2362,858],{},[460,2364,2367],{"href":2365,"rel":2366},"https:\u002F\u002Fgo.dev\u002Fblog\u002Fgo1.13-errors",[864],"Go blog: Working with errors",[469,2369,2371,2372,2375],{"id":2370},"sentinel-definíció-testreszabott-is-sel","Sentinel-definíció testreszabott ",[480,2373,2374],{},"Is","-sel",[448,2377,2378,2380,2381,2384,2385,2387,2388,2391,2392,2395],{},[452,2379,477],{}," egy immutable, stack nélküli ",[480,2382,2383],{},"Definition"," írja le a hiba kódját\u002Fpublikus üzenetét\u002FHTTP-státuszát csomagszintű ",[480,2386,2159],{},"-ként; minden belőle példányosított hiba visszahivatkozik a definícióra, és a hiba ",[480,2389,2390],{},"Is(target)"," metódusa ezt a hivatkozást hasonlítja, így ",[480,2393,2394],{},"errors.Is(err, ErrX)"," a lánc bármely mélységén talál, miközben minden előfordulás a saját, hívás-helyi stacket kapja.",[519,2397,2399],{"className":521,"code":2398,"language":523,"meta":524,"style":524},"\u002F\u002F errs\u002Fdefine.go + errs\u002Ferrs.go\nfunc Define(code string, attrs ...Attr) *Definition { \u002F* ... *\u002F }\n\nfunc (e *Error) Is(target error) bool {\n    d, ok := target.(*Definition)\n    return ok && e.def != nil && e.def == d\n}\n",[480,2400,2401,2406,2442,2446,2478,2500,2538],{"__ignoreMap":524},[528,2402,2403],{"class":530,"line":531},[528,2404,2405],{"class":534},"\u002F\u002F errs\u002Fdefine.go + errs\u002Ferrs.go\n",[528,2407,2408,2410,2413,2415,2417,2419,2421,2424,2427,2430,2432,2434,2436,2438,2440],{"class":530,"line":538},[528,2409,568],{"class":541},[528,2411,2412],{"class":571}," Define",[528,2414,575],{"class":541},[528,2416,480],{"class":578},[528,2418,583],{"class":582},[528,2420,586],{"class":541},[528,2422,2423],{"class":578}," attrs",[528,2425,2426],{"class":541}," ...",[528,2428,2429],{"class":545},"Attr",[528,2431,604],{"class":541},[528,2433,629],{"class":541},[528,2435,2383],{"class":545},[528,2437,636],{"class":541},[528,2439,1140],{"class":534},[528,2441,697],{"class":541},[528,2443,2444],{"class":530,"line":558},[528,2445,562],{"emptyLinePlaceholder":561},[528,2447,2448,2450,2452,2455,2457,2459,2461,2464,2466,2469,2471,2473,2476],{"class":530,"line":565},[528,2449,568],{"class":541},[528,2451,822],{"class":541},[528,2453,2454],{"class":578},"e ",[528,2456,1669],{"class":541},[528,2458,2268],{"class":545},[528,2460,604],{"class":541},[528,2462,2463],{"class":571}," Is",[528,2465,575],{"class":541},[528,2467,2468],{"class":578},"target",[528,2470,607],{"class":582},[528,2472,604],{"class":541},[528,2474,2475],{"class":582}," bool",[528,2477,614],{"class":541},[528,2479,2480,2483,2485,2488,2490,2493,2496,2498],{"class":530,"line":617},[528,2481,2482],{"class":639},"    d",[528,2484,586],{"class":541},[528,2486,2487],{"class":639}," ok ",[528,2489,1160],{"class":541},[528,2491,2492],{"class":639}," target",[528,2494,2495],{"class":541},".(*",[528,2497,2383],{"class":545},[528,2499,555],{"class":541},[528,2501,2502,2504,2506,2509,2512,2514,2517,2520,2523,2526,2528,2530,2532,2535],{"class":530,"line":700},[528,2503,621],{"class":620},[528,2505,2487],{"class":639},[528,2507,2508],{"class":541},"&&",[528,2510,2511],{"class":639}," e",[528,2513,598],{"class":541},[528,2515,2516],{"class":639},"def ",[528,2518,2519],{"class":541},"!=",[528,2521,2522],{"class":541}," nil",[528,2524,2525],{"class":541}," &&",[528,2527,2511],{"class":639},[528,2529,598],{"class":541},[528,2531,2516],{"class":639},[528,2533,2534],{"class":541},"==",[528,2536,2537],{"class":639}," d\n",[528,2539,2540],{"class":530,"line":706},[528,2541,703],{"class":541},[448,2543,2544,859,2546,2552,2553,2556],{},[452,2545,799],{},[460,2547,2549],{"href":2548},"\u002Fhu\u002Ferrs\u002Fapi#errsdefine-neves%C3%ADtett-hibam%C3%B3dok",[480,2550,2551],{},"errs.Define",": a teljes indoklás és a shop ",[480,2554,2555],{},"ErrOutOfStock"," példa a Hibamodell oldalon.",[448,2558,2559,859,2561,2565,2566,2568],{},[452,2560,858],{},[460,2562,2564],{"href":2365,"rel":2563},[864],"Go blog: sentinel errors"," (a ",[480,2567,2383],{}," ennek generalizálása egy attribútum-hordozó változatra).",[469,2570,2572],{"id":2571},"lusta-láncononkénti-stack-capture","Lusta, láncononkénti stack-capture",[448,2574,2575,2577,2578,2581,2582,2585,2586,2589],{},[452,2576,477],{}," a teljes ",[480,2579,2580],{},"runtime.Callers"," stack csak a lánc ",[452,2583,2584],{},"legmélyebb"," hibájánál capture-ölődik (a ",[480,2587,2588],{},"hasStack"," ellenőrzi a cause-t); minden további wrap csak a saját wrap-helyét jegyzi fel. Így egy mélyen wrappelt hiba loggolása sem drágul lineárisan a wrap-szintek számával.",[519,2591,2593],{"className":521,"code":2592,"language":523,"meta":524,"style":524},"\u002F\u002F errs\u002Fstack.go\nconst callerSkip = 4 \u002F\u002F pinelve TestCaptureSkip-pel\n\nfunc newError(cause error, msg, full string) *Error {\n    e := &Error{msg: msg, full: full, err: cause, frame: caller(callerSkip)}\n    if !hasStack(cause) {\n        e.stack = callers(callerSkip)\n    }\n    return e\n}\n",[480,2594,2595,2600,2617,2621,2655,2711,2728,2749,2754,2761],{"__ignoreMap":524},[528,2596,2597],{"class":530,"line":531},[528,2598,2599],{"class":534},"\u002F\u002F errs\u002Fstack.go\n",[528,2601,2602,2605,2608,2610,2614],{"class":530,"line":538},[528,2603,2604],{"class":541},"const",[528,2606,2607],{"class":639}," callerSkip ",[528,2609,648],{"class":541},[528,2611,2613],{"class":2612},"sbssI"," 4",[528,2615,2616],{"class":534}," \u002F\u002F pinelve TestCaptureSkip-pel\n",[528,2618,2619],{"class":530,"line":558},[528,2620,562],{"emptyLinePlaceholder":561},[528,2622,2623,2625,2628,2630,2633,2635,2637,2640,2642,2645,2647,2649,2651,2653],{"class":530,"line":565},[528,2624,568],{"class":541},[528,2626,2627],{"class":571}," newError",[528,2629,575],{"class":541},[528,2631,2632],{"class":578},"cause",[528,2634,607],{"class":582},[528,2636,586],{"class":541},[528,2638,2639],{"class":578}," msg",[528,2641,586],{"class":541},[528,2643,2644],{"class":578}," full",[528,2646,583],{"class":582},[528,2648,604],{"class":541},[528,2650,629],{"class":541},[528,2652,2268],{"class":545},[528,2654,614],{"class":541},[528,2656,2657,2660,2662,2664,2666,2668,2671,2673,2675,2677,2679,2681,2683,2685,2688,2690,2693,2695,2698,2700,2703,2705,2708],{"class":530,"line":617},[528,2658,2659],{"class":639},"    e ",[528,2661,1160],{"class":541},[528,2663,2189],{"class":541},[528,2665,2268],{"class":545},[528,2667,673],{"class":541},[528,2669,2670],{"class":639},"msg",[528,2672,679],{"class":541},[528,2674,2639],{"class":639},[528,2676,586],{"class":541},[528,2678,2644],{"class":639},[528,2680,679],{"class":541},[528,2682,2644],{"class":639},[528,2684,586],{"class":541},[528,2686,2687],{"class":639}," err",[528,2689,679],{"class":541},[528,2691,2692],{"class":639}," cause",[528,2694,586],{"class":541},[528,2696,2697],{"class":639}," frame",[528,2699,679],{"class":541},[528,2701,2702],{"class":571}," caller",[528,2704,575],{"class":541},[528,2706,2707],{"class":639},"callerSkip",[528,2709,2710],{"class":541},")}\n",[528,2712,2713,2716,2718,2720,2722,2724,2726],{"class":530,"line":700},[528,2714,2715],{"class":620},"    if",[528,2717,940],{"class":541},[528,2719,2588],{"class":571},[528,2721,575],{"class":541},[528,2723,2632],{"class":639},[528,2725,604],{"class":541},[528,2727,614],{"class":541},[528,2729,2730,2733,2735,2738,2740,2743,2745,2747],{"class":530,"line":706},[528,2731,2732],{"class":639},"        e",[528,2734,598],{"class":541},[528,2736,2737],{"class":639},"stack ",[528,2739,648],{"class":541},[528,2741,2742],{"class":571}," callers",[528,2744,575],{"class":541},[528,2746,2707],{"class":639},[528,2748,555],{"class":541},[528,2750,2751],{"class":530,"line":745},[528,2752,2753],{"class":541},"    }\n",[528,2755,2756,2758],{"class":530,"line":792},[528,2757,621],{"class":620},[528,2759,2760],{"class":639}," e\n",[528,2762,2763],{"class":530,"line":1105},[528,2764,703],{"class":541},[448,2766,2767,859,2769,2774,2775,495,2778,2781],{},[452,2768,799],{},[460,2770,2772],{"href":2771},"\u002Fhu\u002Ferrs\u002Fapi#stack-szemantika",[480,2773,358],{},": a ",[480,2776,2777],{},"Frames",[480,2779,2780],{},"Chain"," accessorok ezen a mechanizmuson keresztül olvasnak.",[448,2783,2784,859,2786,598],{},[452,2785,858],{},[460,2787,2790],{"href":2788,"rel":2789},"https:\u002F\u002Fpkg.go.dev\u002Fruntime#Callers",[864],"pkg.dev: runtime.Callers",[464,2792,2794],{"id":2793},"adatbázis","Adatbázis",[469,2796,2798],{"id":2797},"szűk-repository-interfész-sqlc-kompatibilis","Szűk repository-interfész (sqlc-kompatibilis)",[448,2800,2801,2803,2804,495,2807,495,2810,2813],{},[452,2802,477],{}," a repositoryk egy minimális, 3 metódusos interfésztől (",[480,2805,2806],{},"Exec",[480,2808,2809],{},"Query",[480,2811,2812],{},"QueryRow",") függenek a konkrét pool helyett, az interfészt nem a driver, hanem a fogyasztó (a repository) igénye definiálja.",[519,2815,2817],{"className":521,"code":2816,"language":523,"meta":524,"style":524},"\u002F\u002F dbx\u002Fpg\u002Fdb.go\ntype DBTX interface {\n    Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)\n    Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)\n    QueryRow(ctx context.Context, sql string, args ...any) pgx.Row\n}\n",[480,2818,2819,2824,2836,2885,2931,2970],{"__ignoreMap":524},[528,2820,2821],{"class":530,"line":531},[528,2822,2823],{"class":534},"\u002F\u002F dbx\u002Fpg\u002Fdb.go\n",[528,2825,2826,2828,2831,2834],{"class":530,"line":538},[528,2827,542],{"class":541},[528,2829,2830],{"class":545}," DBTX",[528,2832,2833],{"class":541}," interface",[528,2835,614],{"class":541},[528,2837,2838,2841,2843,2845,2847,2849,2851,2853,2856,2858,2860,2863,2865,2867,2869,2871,2874,2876,2879,2881,2883],{"class":530,"line":558},[528,2839,2840],{"class":571},"    Exec",[528,2842,575],{"class":541},[528,2844,1256],{"class":578},[528,2846,1259],{"class":545},[528,2848,598],{"class":541},[528,2850,601],{"class":545},[528,2852,586],{"class":541},[528,2854,2855],{"class":578}," sql",[528,2857,583],{"class":582},[528,2859,586],{"class":541},[528,2861,2862],{"class":578}," args",[528,2864,2426],{"class":541},[528,2866,1301],{"class":545},[528,2868,604],{"class":541},[528,2870,822],{"class":541},[528,2872,2873],{"class":545},"pgconn",[528,2875,598],{"class":541},[528,2877,2878],{"class":545},"CommandTag",[528,2880,586],{"class":541},[528,2882,607],{"class":582},[528,2884,555],{"class":541},[528,2886,2887,2890,2892,2894,2896,2898,2900,2902,2904,2906,2908,2910,2912,2914,2916,2918,2920,2922,2925,2927,2929],{"class":530,"line":565},[528,2888,2889],{"class":571},"    Query",[528,2891,575],{"class":541},[528,2893,1256],{"class":578},[528,2895,1259],{"class":545},[528,2897,598],{"class":541},[528,2899,601],{"class":545},[528,2901,586],{"class":541},[528,2903,2855],{"class":578},[528,2905,583],{"class":582},[528,2907,586],{"class":541},[528,2909,2862],{"class":578},[528,2911,2426],{"class":541},[528,2913,1301],{"class":545},[528,2915,604],{"class":541},[528,2917,822],{"class":541},[528,2919,414],{"class":545},[528,2921,598],{"class":541},[528,2923,2924],{"class":545},"Rows",[528,2926,586],{"class":541},[528,2928,607],{"class":582},[528,2930,555],{"class":541},[528,2932,2933,2936,2938,2940,2942,2944,2946,2948,2950,2952,2954,2956,2958,2960,2962,2965,2967],{"class":530,"line":617},[528,2934,2935],{"class":571},"    QueryRow",[528,2937,575],{"class":541},[528,2939,1256],{"class":578},[528,2941,1259],{"class":545},[528,2943,598],{"class":541},[528,2945,601],{"class":545},[528,2947,586],{"class":541},[528,2949,2855],{"class":578},[528,2951,583],{"class":582},[528,2953,586],{"class":541},[528,2955,2862],{"class":578},[528,2957,2426],{"class":541},[528,2959,1301],{"class":545},[528,2961,604],{"class":541},[528,2963,2964],{"class":545}," pgx",[528,2966,598],{"class":541},[528,2968,2969],{"class":545},"Row\n",[528,2971,2972],{"class":530,"line":700},[528,2973,703],{"class":541},[448,2975,2976,2978,2979,2982,2983,2988,2989,2992],{},[452,2977,504],{}," ez az interfész ",[452,2980,2981],{},"metódusról metódusra azonos"," azzal, amit a ",[460,2984,2987],{"href":2985,"rel":2986},"https:\u002F\u002Fsqlc.dev",[864],"sqlc"," generál, így egy sqlc-vel generált repository a kit ",[480,2990,2991],{},"pg.DB","-jét változtatás nélkül elfogadja, és ugyanabban a context-hordozta tranzakcióban vesz részt, mint a kézzel írt kód.",[448,2994,2995,859,2997,598],{},[452,2996,799],{},[460,2998,3000],{"href":2999},"\u002Fhu\u002Fdbx\u002Fpg#dbtx--a-repositoryk-query-fel%C3%BClete",[480,3001,3002],{},"dbx\u002Fpg.DBTX",[448,3004,3005,859,3007,3012],{},[452,3006,858],{},[460,3008,3011],{"href":3009,"rel":3010},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Feffective_go#interfaces",[864],"Go, Effective Go: Interfaces"," (kis, fogyasztó-definiált interfészek elve).",[469,3014,3016],{"id":3015},"unit-of-work-tranzakció-a-contextben","Unit of Work \u002F tranzakció a contextben",[448,3018,3019,487,3021,3024,3025,3027,3028,3031],{},[452,3020,477],{},[480,3022,3023],{},"Transactor.WithinTransaction(ctx, fn)"," nyit tranzakciót, a tranzakciót a ",[480,3026,1256],{},"-be teszi, és a ",[480,3029,3030],{},"fn"," lefutása után commitol vagy (hiba vagy panic esetén) rollbackel. Beágyazott hívás (ha a context már hordoz tranzakciót) a meglévőhöz csatlakozik ahelyett, hogy újat nyitna: a nesting lapos, a commit\u002Frollback mindig a legkülső hívást illeti.",[519,3033,3035],{"className":521,"code":3034,"language":523,"meta":524,"style":524},"\u002F\u002F dbx\u002Fpg\u002Ftransactor.go\nfunc (t *transactor) WithinTransaction(ctx context.Context, fn func(ctx context.Context) error) error {\n    if _, ok := TxFromContext(ctx); ok {\n        return fn(ctx) \u002F\u002F csatlakozás a meglévőhöz\n    }\n    tx, err := t.pool.Begin(ctx)\n    \u002F\u002F ...\n    committed := false\n    defer func() {\n        if !committed {\n            _ = tx.Rollback(context.WithoutCancel(ctx)) \u002F\u002F panic-safe\n        }\n    }()\n    if err := fn(ContextWithTx(ctx, tx)); err != nil {\n        return err\n    }\n    if err := tx.Commit(ctx); err != nil { \u002F* ... *\u002F }\n    committed = true\n    return nil\n}\n",[480,3036,3037,3042,3095,3121,3137,3141,3172,3176,3187,3197,3209,3243,3248,3253,3287,3295,3300,3334,3344,3352],{"__ignoreMap":524},[528,3038,3039],{"class":530,"line":531},[528,3040,3041],{"class":534},"\u002F\u002F dbx\u002Fpg\u002Ftransactor.go\n",[528,3043,3044,3046,3048,3051,3053,3056,3058,3061,3063,3065,3067,3069,3071,3073,3075,3077,3079,3081,3083,3085,3087,3089,3091,3093],{"class":530,"line":538},[528,3045,568],{"class":541},[528,3047,822],{"class":541},[528,3049,3050],{"class":578},"t ",[528,3052,1669],{"class":541},[528,3054,3055],{"class":545},"transactor",[528,3057,604],{"class":541},[528,3059,3060],{"class":571}," WithinTransaction",[528,3062,575],{"class":541},[528,3064,1256],{"class":578},[528,3066,1259],{"class":545},[528,3068,598],{"class":541},[528,3070,601],{"class":545},[528,3072,586],{"class":541},[528,3074,589],{"class":578},[528,3076,592],{"class":541},[528,3078,1256],{"class":578},[528,3080,1259],{"class":545},[528,3082,598],{"class":541},[528,3084,601],{"class":545},[528,3086,604],{"class":541},[528,3088,607],{"class":582},[528,3090,604],{"class":541},[528,3092,607],{"class":582},[528,3094,614],{"class":541},[528,3096,3097,3099,3101,3103,3105,3107,3110,3112,3114,3117,3119],{"class":530,"line":558},[528,3098,2715],{"class":620},[528,3100,1152],{"class":639},[528,3102,586],{"class":541},[528,3104,2487],{"class":639},[528,3106,1160],{"class":541},[528,3108,3109],{"class":571}," TxFromContext",[528,3111,575],{"class":541},[528,3113,1256],{"class":639},[528,3115,3116],{"class":541},");",[528,3118,2487],{"class":639},[528,3120,950],{"class":541},[528,3122,3123,3126,3128,3130,3132,3134],{"class":530,"line":565},[528,3124,3125],{"class":620},"        return",[528,3127,589],{"class":571},[528,3129,575],{"class":541},[528,3131,1256],{"class":639},[528,3133,604],{"class":541},[528,3135,3136],{"class":534}," \u002F\u002F csatlakozás a meglévőhöz\n",[528,3138,3139],{"class":530,"line":617},[528,3140,2753],{"class":541},[528,3142,3143,3146,3148,3151,3153,3156,3158,3161,3163,3166,3168,3170],{"class":530,"line":700},[528,3144,3145],{"class":639},"    tx",[528,3147,586],{"class":541},[528,3149,3150],{"class":639}," err ",[528,3152,1160],{"class":541},[528,3154,3155],{"class":639}," t",[528,3157,598],{"class":541},[528,3159,3160],{"class":639},"pool",[528,3162,598],{"class":541},[528,3164,3165],{"class":571},"Begin",[528,3167,575],{"class":541},[528,3169,1256],{"class":639},[528,3171,555],{"class":541},[528,3173,3174],{"class":530,"line":706},[528,3175,1356],{"class":534},[528,3177,3178,3181,3183],{"class":530,"line":745},[528,3179,3180],{"class":639},"    committed ",[528,3182,1160],{"class":541},[528,3184,3186],{"class":3185},"sfNiH"," false\n",[528,3188,3189,3192,3195],{"class":530,"line":792},[528,3190,3191],{"class":620},"    defer",[528,3193,3194],{"class":541}," func()",[528,3196,614],{"class":541},[528,3198,3199,3202,3204,3207],{"class":530,"line":1105},[528,3200,3201],{"class":620},"        if",[528,3203,940],{"class":541},[528,3205,3206],{"class":639},"committed ",[528,3208,950],{"class":541},[528,3210,3211,3214,3216,3219,3221,3224,3226,3228,3230,3233,3235,3237,3240],{"class":530,"line":1111},[528,3212,3213],{"class":639},"            _ ",[528,3215,648],{"class":541},[528,3217,3218],{"class":639}," tx",[528,3220,598],{"class":541},[528,3222,3223],{"class":571},"Rollback",[528,3225,575],{"class":541},[528,3227,595],{"class":639},[528,3229,598],{"class":541},[528,3231,3232],{"class":571},"WithoutCancel",[528,3234,575],{"class":541},[528,3236,1256],{"class":639},[528,3238,3239],{"class":541},"))",[528,3241,3242],{"class":534}," \u002F\u002F panic-safe\n",[528,3244,3245],{"class":530,"line":1146},[528,3246,3247],{"class":541},"        }\n",[528,3249,3250],{"class":530,"line":1174},[528,3251,3252],{"class":541},"    }()\n",[528,3254,3255,3257,3259,3261,3263,3265,3268,3270,3272,3274,3276,3279,3281,3283,3285],{"class":530,"line":1189},[528,3256,2715],{"class":620},[528,3258,3150],{"class":639},[528,3260,1160],{"class":541},[528,3262,589],{"class":571},[528,3264,575],{"class":541},[528,3266,3267],{"class":571},"ContextWithTx",[528,3269,575],{"class":541},[528,3271,1256],{"class":639},[528,3273,586],{"class":541},[528,3275,3218],{"class":639},[528,3277,3278],{"class":541},"));",[528,3280,3150],{"class":639},[528,3282,2519],{"class":541},[528,3284,2522],{"class":541},[528,3286,614],{"class":541},[528,3288,3290,3292],{"class":530,"line":3289},15,[528,3291,3125],{"class":620},[528,3293,3294],{"class":639}," err\n",[528,3296,3298],{"class":530,"line":3297},16,[528,3299,2753],{"class":541},[528,3301,3303,3305,3307,3309,3311,3313,3316,3318,3320,3322,3324,3326,3328,3330,3332],{"class":530,"line":3302},17,[528,3304,2715],{"class":620},[528,3306,3150],{"class":639},[528,3308,1160],{"class":541},[528,3310,3218],{"class":639},[528,3312,598],{"class":541},[528,3314,3315],{"class":571},"Commit",[528,3317,575],{"class":541},[528,3319,1256],{"class":639},[528,3321,3116],{"class":541},[528,3323,3150],{"class":639},[528,3325,2519],{"class":541},[528,3327,2522],{"class":541},[528,3329,636],{"class":541},[528,3331,1140],{"class":534},[528,3333,697],{"class":541},[528,3335,3337,3339,3341],{"class":530,"line":3336},18,[528,3338,3180],{"class":639},[528,3340,648],{"class":541},[528,3342,3343],{"class":3185}," true\n",[528,3345,3347,3349],{"class":530,"line":3346},19,[528,3348,621],{"class":620},[528,3350,3351],{"class":541}," nil\n",[528,3353,3355],{"class":530,"line":3354},20,[528,3356,703],{"class":541},[448,3358,3359,3361,3362,3365],{},[452,3360,504],{}," a service-réteg nem akar tudni pgx-ről vagy bunról, csak azt akarja mondani, hogy \"ez a több lépés egy tranzakcióban fusson\", és a repositoryk (amik a ",[480,3363,3364],{},"DBTX","-től függenek, nem a tranzakciótól) automatikusan csatlakozzanak.",[448,3367,3368,859,3370,3375,3376,1592,3380,598],{},[452,3369,799],{},[460,3371,3372],{"href":423},[480,3373,3374],{},"dbx.Transactor",", implementációk: ",[460,3377,3378],{"href":415},[480,3379,1591],{},[460,3381,3382],{"href":419},[480,3383,1595],{},[448,3385,3386,859,3388,598],{},[452,3387,858],{},[460,3389,3392],{"href":3390,"rel":3391},"https:\u002F\u002Fmartinfowler.com\u002FeaaCatalog\u002FunitOfWork.html",[864],"martinfowler.com: Unit of Work",[469,3394,3396],{"id":3395},"opaque-manipuláció-védett-cursor","Opaque, manipuláció-védett cursor",[448,3398,3399,3401,3402,3405,3406,3409],{},[452,3400,477],{}," a keyset-lapozás \"hol tartottam\" állapota egy base64url-kódolt JSON-boríték, ami a legutolsó sor rendező kulcsait ",[452,3403,3404],{},"és"," egy sha256-fingerprintet hordoz a normalizált ",[480,3407,3408],{},"ORDER BY","-ról, ha a kliens egy másik sorrendhez próbálja visszaküldeni a cursort, a fingerprint-eltérés miatt elutasításra kerül, ahelyett, hogy hibás oldalt adna vissza.",[519,3411,3413],{"className":521,"code":3412,"language":523,"meta":524,"style":524},"\u002F\u002F paginate\u002Fcursor.go\nfunc sortFingerprint(sort string) string {\n    \u002F\u002F normalizált ORDER BY → rövid sha256 hex\n}\nif env.S != sortFingerprint(sort) {\n    return CursorParams{}, ErrInvalidCursor.New(\"cursor issued for a different sort order\")\n}\n",[480,3414,3415,3420,3440,3445,3449,3473,3501],{"__ignoreMap":524},[528,3416,3417],{"class":530,"line":531},[528,3418,3419],{"class":534},"\u002F\u002F paginate\u002Fcursor.go\n",[528,3421,3422,3424,3427,3429,3432,3434,3436,3438],{"class":530,"line":538},[528,3423,568],{"class":541},[528,3425,3426],{"class":571}," sortFingerprint",[528,3428,575],{"class":541},[528,3430,3431],{"class":578},"sort",[528,3433,583],{"class":582},[528,3435,604],{"class":541},[528,3437,583],{"class":582},[528,3439,614],{"class":541},[528,3441,3442],{"class":530,"line":558},[528,3443,3444],{"class":534},"    \u002F\u002F normalizált ORDER BY → rövid sha256 hex\n",[528,3446,3447],{"class":530,"line":565},[528,3448,703],{"class":541},[528,3450,3451,3453,3456,3458,3461,3463,3465,3467,3469,3471],{"class":530,"line":617},[528,3452,896],{"class":620},[528,3454,3455],{"class":639}," env",[528,3457,598],{"class":541},[528,3459,3460],{"class":639},"S ",[528,3462,2519],{"class":541},[528,3464,3426],{"class":571},[528,3466,575],{"class":541},[528,3468,3431],{"class":639},[528,3470,604],{"class":541},[528,3472,614],{"class":541},[528,3474,3475,3477,3480,3482,3485,3487,3489,3491,3494,3497,3499],{"class":530,"line":700},[528,3476,621],{"class":620},[528,3478,3479],{"class":545}," CursorParams",[528,3481,1492],{"class":541},[528,3483,3484],{"class":639}," ErrInvalidCursor",[528,3486,598],{"class":541},[528,3488,494],{"class":571},[528,3490,575],{"class":541},[528,3492,3493],{"class":541},"\"",[528,3495,3496],{"class":2002},"cursor issued for a different sort order",[528,3498,3493],{"class":541},[528,3500,555],{"class":541},[528,3502,3503],{"class":530,"line":706},[528,3504,703],{"class":541},[448,3506,3507,859,3509,2774,3517,495,3520,3523],{},[452,3508,799],{},[460,3510,3511,495,3514],{"href":440},[480,3512,3513],{},"paginate.CursorParams",[480,3515,3516],{},"NormalizeCursor",[480,3518,3519],{},"Page[T]",[480,3521,3522],{},"CursorPage[T]"," generikus wrapperekkel együtt.",[448,3525,3526,859,3528,3533,3534,3537],{},[452,3527,858],{},[460,3529,3532],{"href":3530,"rel":3531},"https:\u002F\u002Fuse-the-index-luke.com\u002Fno-offset",[864],"use-the-index-luke.com: keyset pagination"," (miért jobb a keyset-lapozás az ",[480,3535,3536],{},"OFFSET","-nél nagy táblákon).",[464,3539,3541],{"id":3540},"aszinkron-feldolgozás","Aszinkron feldolgozás",[469,3543,3545],{"id":3544},"tranzakciós-outbox","Tranzakciós outbox",[448,3547,3548,3550,3551,3554],{},[452,3549,477],{}," az esemény nem közvetlenül a message brokerbe kerül, hanem egy adatbázis-táblába, ",[452,3552,3553],{},"ugyanabban a tranzakcióban",", mint az üzleti írás, egy külön relay-folyamat pedig áttolja a commitált sorokat a queue-ba. Ez zárja be azt a rést, ahol a DB-írás megtörténik, de az esemény-küldés elveszik (vagy fordítva).",[519,3556,3560],{"className":3557,"code":3558,"language":3559,"meta":524,"style":524},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","-- events\u002Foutbox\u002Frelay.go\nSELECT id, event_id, event_name, payload, metadata, created_at\nFROM outbox_events\nWHERE published_at IS NULL\nORDER BY id\nLIMIT $1\nFOR UPDATE SKIP LOCKED\n","sql",[480,3561,3562,3567,3575,3583,3597,3603,3614],{"__ignoreMap":524},[528,3563,3564],{"class":530,"line":531},[528,3565,3566],{"class":534},"-- events\u002Foutbox\u002Frelay.go\n",[528,3568,3569,3572],{"class":530,"line":538},[528,3570,3571],{"class":2612},"SELECT",[528,3573,3574],{"class":639}," id, event_id, event_name, payload, metadata, created_at\n",[528,3576,3577,3580],{"class":530,"line":558},[528,3578,3579],{"class":2612},"FROM",[528,3581,3582],{"class":639}," outbox_events\n",[528,3584,3585,3588,3591,3594],{"class":530,"line":565},[528,3586,3587],{"class":2612},"WHERE",[528,3589,3590],{"class":639}," published_at ",[528,3592,3593],{"class":2612},"IS",[528,3595,3596],{"class":2612}," NULL\n",[528,3598,3599,3601],{"class":530,"line":617},[528,3600,3408],{"class":2612},[528,3602,1564],{"class":639},[528,3604,3605,3608,3611],{"class":530,"line":700},[528,3606,3607],{"class":2612},"LIMIT",[528,3609,3610],{"class":639}," $",[528,3612,3613],{"class":2612},"1\n",[528,3615,3616,3619,3622,3625],{"class":530,"line":706},[528,3617,3618],{"class":2612},"FOR",[528,3620,3621],{"class":2612}," UPDATE",[528,3623,3624],{"class":2612}," SKIP",[528,3626,3627],{"class":639}," LOCKED\n",[448,3629,3630,3632,3633,3636],{},[452,3631,504],{}," N worker-replika konkurrensen pollozhatja ugyanazt a táblát, a ",[480,3634,3635],{},"FOR UPDATE SKIP LOCKED"," garantálja, hogy minden replika diszjunkt batch-et foglal, blokkolás nélkül.",[448,3638,3639,859,3641,3646,3647,3650],{},[452,3640,799],{},[460,3642,3643],{"href":73},[480,3644,3645],{},"events\u002Foutbox",": a teljes dual-write probléma és a shop ",[480,3648,3649],{},"PlaceOrder"," példája ott.",[448,3652,3653,859,3655,598],{},[452,3654,858],{},[460,3656,3659],{"href":3657,"rel":3658},"https:\u002F\u002Fmicroservices.io\u002Fpatterns\u002Fdata\u002Ftransactional-outbox.html",[864],"microservices.io: Transactional outbox",[469,3661,3663],{"id":3662},"legalább-egyszeres-kézbesítés-idempotens-listener","Legalább-egyszeres kézbesítés + idempotens listener",[448,3665,3666,3668,3669,3672,3673,3676],{},[452,3667,477],{}," a kézbesítési lánc (outbox → relay → queue → listener) végig ",[452,3670,3671],{},"at-least-once",": retry, crash-újrapróbálkozás miatt egy esemény többször is eljuthat a listenerhez. A szerződés ezért explicit: a listener legyen idempotens, egy stabil per-esemény azonosítót (",[480,3674,3675],{},"Meta.ID",") használva dedup-kulcsként.",[519,3678,3680],{"className":521,"code":3679,"language":523,"meta":524,"style":524},"\u002F\u002F events\u002Fevents.go\n\u002F\u002F Delivery is at-least-once end to end (outbox → relay → asynq → listener), so\n\u002F\u002F LISTENERS MUST BE IDEMPOTENT. Use Meta(ctx).ID as an idempotency key.\n",[480,3681,3682,3687,3692],{"__ignoreMap":524},[528,3683,3684],{"class":530,"line":531},[528,3685,3686],{"class":534},"\u002F\u002F events\u002Fevents.go\n",[528,3688,3689],{"class":530,"line":538},[528,3690,3691],{"class":534},"\u002F\u002F Delivery is at-least-once end to end (outbox → relay → asynq → listener), so\n",[528,3693,3694],{"class":530,"line":558},[528,3695,3696],{"class":534},"\u002F\u002F LISTENERS MUST BE IDEMPOTENT. Use Meta(ctx).ID as an idempotency key.\n",[448,3698,3699,859,3701,2774,3705,3707,3708,3711,3712,3715],{},[452,3700,799],{},[460,3702,3703],{"href":68},[480,3704,61],{},[480,3706,3675],{}," és a ",[480,3709,3710],{},"FanoutTasks"," determinisztikus ",[480,3713,3714],{},"TaskID","-generálása.",[448,3717,3718,859,3720,598],{},[452,3719,858],{},[460,3721,3724],{"href":3722,"rel":3723},"https:\u002F\u002Fmicroservices.io\u002Fpatterns\u002Fcommunication-style\u002Fidempotent-consumer.html",[864],"microservices.io: Idempotent Consumer",[469,3726,3728],{"id":3727},"valkey-lease-leader-election","Valkey-lease leader election",[448,3730,3731,3733,3734,3737,3738,3741],{},[452,3732,477],{}," egy egyetlen-birtokosú lock Valkeyben (",[480,3735,3736],{},"SETNX"," a szerzéshez, egy Lua-script a megújításhoz, ami csak akkor engedi meg a ",[480,3739,3740],{},"PEXPIRE","-t, ha a hívó azonosítója még mindig a lock-értékkel egyezik), így egy replika-halmazból pontosan egy \"vezető\" fut egy adott ütemezett feladatot.",[519,3743,3745],{"className":521,"code":3744,"language":523,"meta":524,"style":524},"\u002F\u002F events\u002Fscheduler\u002Flease.go\nvar renewScript = redis.NewScript(`\nif redis.call(\"get\", KEYS[1]) == ARGV[1] then\n  return redis.call(\"pexpire\", KEYS[1], ARGV[2])\nelse\n  return 0\nend`)\n",[480,3746,3747,3752,3773,3778,3783,3788,3793],{"__ignoreMap":524},[528,3748,3749],{"class":530,"line":531},[528,3750,3751],{"class":534},"\u002F\u002F events\u002Fscheduler\u002Flease.go\n",[528,3753,3754,3756,3759,3761,3764,3766,3769,3771],{"class":530,"line":538},[528,3755,2159],{"class":541},[528,3757,3758],{"class":639}," renewScript ",[528,3760,648],{"class":541},[528,3762,3763],{"class":639}," redis",[528,3765,598],{"class":541},[528,3767,3768],{"class":571},"NewScript",[528,3770,575],{"class":541},[528,3772,2006],{"class":541},[528,3774,3775],{"class":530,"line":558},[528,3776,3777],{"class":2002},"if redis.call(\"get\", KEYS[1]) == ARGV[1] then\n",[528,3779,3780],{"class":530,"line":565},[528,3781,3782],{"class":2002},"  return redis.call(\"pexpire\", KEYS[1], ARGV[2])\n",[528,3784,3785],{"class":530,"line":617},[528,3786,3787],{"class":2002},"else\n",[528,3789,3790],{"class":530,"line":700},[528,3791,3792],{"class":2002},"  return 0\n",[528,3794,3795,3798,3801],{"class":530,"line":706},[528,3796,3797],{"class":2002},"end",[528,3799,3800],{"class":541},"`",[528,3802,555],{"class":541},[448,3804,3805,859,3807,2774,3812,3815],{},[452,3806,799],{},[460,3808,3809],{"href":78},[480,3810,3811],{},"scheduler",[480,3813,3814],{},"Runner.lead"," renewel a TTL harmadánál, és lemond a vezetésről, ha elveszti.",[448,3817,3818,859,3820,598],{},[452,3819,858],{},[460,3821,3824],{"href":3822,"rel":3823},"https:\u002F\u002Fredis.io\u002Fdocs\u002Flatest\u002Fdevelop\u002Fuse\u002Fpatterns\u002Fdistributed-locks\u002F",[864],"Redis docs: Distributed locks",[464,3826,3828],{"id":3827},"biztonság","Biztonság",[469,3830,3832],{"id":3831},"generikusok-a-claims-típus-fölött","Generikusok a claims-típus fölött",[448,3834,3835,3837,3838,487,3841,3844,3845,3848],{},[452,3836,477],{}," a JWT-kibocsátó és az ",[480,3839,3840],{},"Identify",[480,3842,3843],{},"[C jwt.Claims]"," típusparaméterrel generikus, a beépített ",[480,3846,3847],{},"DefaultClaims"," mellett saját claims-struct is használható, ugyanazzal az API-val és ugyanazokkal a validációs garanciákkal (algoritmus-pinning, lejárat-ellenőrzés).",[519,3850,3852],{"className":521,"code":3851,"language":523,"meta":524,"style":524},"\u002F\u002F auth\u002Ftoken.go\ntype TokenIssuer[C jwt.Claims] struct{ cfg Config }\n\n\u002F\u002F auth\u002Fidentify.go\nfunc Identify[C jwt.Claims](issuer *TokenIssuer[C], toIdentity func(C) (*rbac.Identity, error), authorization string) (*rbac.Identity, error)\n",[480,3853,3854,3859,3891,3895,3900],{"__ignoreMap":524},[528,3855,3856],{"class":530,"line":531},[528,3857,3858],{"class":534},"\u002F\u002F auth\u002Ftoken.go\n",[528,3860,3861,3863,3866,3868,3871,3874,3876,3879,3882,3884,3887,3889],{"class":530,"line":538},[528,3862,542],{"class":541},[528,3864,3865],{"class":545}," TokenIssuer",[528,3867,1247],{"class":541},[528,3869,3870],{"class":578},"C",[528,3872,3873],{"class":545}," jwt",[528,3875,598],{"class":541},[528,3877,3878],{"class":545},"Claims",[528,3880,3881],{"class":541},"]",[528,3883,1663],{"class":541},[528,3885,3886],{"class":639}," cfg ",[528,3888,516],{"class":545},[528,3890,697],{"class":541},[528,3892,3893],{"class":530,"line":558},[528,3894,562],{"emptyLinePlaceholder":561},[528,3896,3897],{"class":530,"line":565},[528,3898,3899],{"class":534},"\u002F\u002F auth\u002Fidentify.go\n",[528,3901,3902,3904,3907,3909,3911,3913,3915,3917,3920,3923,3925,3928,3930,3932,3935,3938,3940,3942,3944,3947,3950,3952,3954,3956,3958,3961,3964,3966,3968,3970,3972,3974,3976,3978,3980],{"class":530,"line":617},[528,3903,568],{"class":541},[528,3905,3906],{"class":571}," Identify",[528,3908,1247],{"class":541},[528,3910,3870],{"class":578},[528,3912,3873],{"class":545},[528,3914,598],{"class":541},[528,3916,3878],{"class":545},[528,3918,3919],{"class":541},"](",[528,3921,3922],{"class":578},"issuer",[528,3924,629],{"class":541},[528,3926,3927],{"class":545},"TokenIssuer",[528,3929,1247],{"class":541},[528,3931,3870],{"class":545},[528,3933,3934],{"class":541},"],",[528,3936,3937],{"class":578}," toIdentity",[528,3939,592],{"class":541},[528,3941,3870],{"class":545},[528,3943,604],{"class":541},[528,3945,3946],{"class":541}," (*",[528,3948,3949],{"class":545},"rbac",[528,3951,598],{"class":541},[528,3953,1460],{"class":545},[528,3955,586],{"class":541},[528,3957,607],{"class":582},[528,3959,3960],{"class":541},"),",[528,3962,3963],{"class":578}," authorization",[528,3965,583],{"class":582},[528,3967,604],{"class":541},[528,3969,3946],{"class":541},[528,3971,3949],{"class":545},[528,3973,598],{"class":541},[528,3975,1460],{"class":545},[528,3977,586],{"class":541},[528,3979,607],{"class":582},[528,3981,555],{"class":541},[448,3983,3984,859,3986,598],{},[452,3985,799],{},[460,3987,3989,809,3992],{"href":3988},"\u002Fhu\u002Fauth\u002Foverview#saj%C3%A1t-claims",[480,3990,3991],{},"auth.TokenIssuer[C]",[480,3993,3994],{},"Identify[C]",[448,3996,3997,859,3999,598],{},[452,3998,858],{},[460,4000,4002],{"href":1387,"rel":4001},[864],"Go generikusok tutorial",[469,4004,4006],{"id":4005},"magasabb-rendű-authorizációs-guard","Magasabb-rendű authorizációs guard",[448,4008,4009,4011,4012,809,4015,4018,4019,4022,4023,4026,4027,4030,4031,809,4034,4037,4038,4041],{},[452,4010,477],{}," a szerep- és permission-ellenőrző függvények (",[480,4013,4014],{},"rbac.CheckRole",[480,4016,4017],{},"rbac.CheckPermission",") mindketten egyetlen ",[480,4020,4021],{},"check(id *Identity, want []string, has func(*Identity, ...string) bool) error"," helpert hívnak, ami az unauthenticated\u002Fforbidden-döntést birtokolja: az egyetlen különbség a kettő között a ",[480,4024,4025],{},"has"," predikátum. A kit ",[480,4028,4029],{},"net\u002Fhttp"," guardjai (",[480,4032,4033],{},"server.RequireRole",[480,4035,4036],{},"server.RequirePermission",") ugyanezt az alakot alkalmazzák eggyel feljebb, egy közös ",[480,4039,4040],{},"requireFn"," fölött, ami a visszaadott errort Problemként rendereli.",[519,4043,4045],{"className":521,"code":4044,"language":523,"meta":524,"style":524},"\u002F\u002F auth\u002Frbac\u002Frbac.go\nfunc CheckRole(id *Identity, roles ...string) error {\n    return check(id, roles, (*Identity).HasRole)\n}\nfunc check(id *Identity, want []string, has func(*Identity, ...string) bool) error {\n    if id == nil {\n        return ErrUnauthenticated\n    }\n    if !has(id, want...) {\n        return ErrForbidden\n    }\n    return nil\n}\n",[480,4046,4047,4052,4083,4111,4115,4164,4177,4184,4188,4208,4215,4219,4225],{"__ignoreMap":524},[528,4048,4049],{"class":530,"line":531},[528,4050,4051],{"class":534},"\u002F\u002F auth\u002Frbac\u002Frbac.go\n",[528,4053,4054,4056,4059,4061,4064,4066,4068,4070,4073,4075,4077,4079,4081],{"class":530,"line":538},[528,4055,568],{"class":541},[528,4057,4058],{"class":571}," CheckRole",[528,4060,575],{"class":541},[528,4062,4063],{"class":578},"id",[528,4065,629],{"class":541},[528,4067,1460],{"class":545},[528,4069,586],{"class":541},[528,4071,4072],{"class":578}," roles",[528,4074,2426],{"class":541},[528,4076,1996],{"class":582},[528,4078,604],{"class":541},[528,4080,607],{"class":582},[528,4082,614],{"class":541},[528,4084,4085,4087,4090,4092,4094,4096,4098,4100,4102,4104,4106,4109],{"class":530,"line":558},[528,4086,621],{"class":620},[528,4088,4089],{"class":571}," check",[528,4091,575],{"class":541},[528,4093,4063],{"class":639},[528,4095,586],{"class":541},[528,4097,4072],{"class":639},[528,4099,586],{"class":541},[528,4101,3946],{"class":541},[528,4103,1460],{"class":639},[528,4105,853],{"class":541},[528,4107,4108],{"class":639},"HasRole",[528,4110,555],{"class":541},[528,4112,4113],{"class":530,"line":565},[528,4114,703],{"class":541},[528,4116,4117,4119,4121,4123,4125,4127,4129,4131,4134,4137,4139,4141,4144,4146,4148,4150,4152,4154,4156,4158,4160,4162],{"class":530,"line":617},[528,4118,568],{"class":541},[528,4120,4089],{"class":571},[528,4122,575],{"class":541},[528,4124,4063],{"class":578},[528,4126,629],{"class":541},[528,4128,1460],{"class":545},[528,4130,586],{"class":541},[528,4132,4133],{"class":578}," want",[528,4135,4136],{"class":541}," []",[528,4138,1996],{"class":582},[528,4140,586],{"class":541},[528,4142,4143],{"class":578}," has",[528,4145,549],{"class":541},[528,4147,1460],{"class":545},[528,4149,586],{"class":541},[528,4151,2426],{"class":541},[528,4153,1996],{"class":582},[528,4155,604],{"class":541},[528,4157,2475],{"class":582},[528,4159,604],{"class":541},[528,4161,607],{"class":582},[528,4163,614],{"class":541},[528,4165,4166,4168,4171,4173,4175],{"class":530,"line":700},[528,4167,2715],{"class":620},[528,4169,4170],{"class":639}," id ",[528,4172,2534],{"class":541},[528,4174,2522],{"class":541},[528,4176,614],{"class":541},[528,4178,4179,4181],{"class":530,"line":706},[528,4180,3125],{"class":620},[528,4182,4183],{"class":639}," ErrUnauthenticated\n",[528,4185,4186],{"class":530,"line":745},[528,4187,2753],{"class":541},[528,4189,4190,4192,4194,4196,4198,4200,4202,4204,4206],{"class":530,"line":792},[528,4191,2715],{"class":620},[528,4193,940],{"class":541},[528,4195,4025],{"class":571},[528,4197,575],{"class":541},[528,4199,4063],{"class":639},[528,4201,586],{"class":541},[528,4203,4133],{"class":639},[528,4205,787],{"class":541},[528,4207,614],{"class":541},[528,4209,4210,4212],{"class":530,"line":1105},[528,4211,3125],{"class":620},[528,4213,4214],{"class":639}," ErrForbidden\n",[528,4216,4217],{"class":530,"line":1111},[528,4218,2753],{"class":541},[528,4220,4221,4223],{"class":530,"line":1146},[528,4222,621],{"class":620},[528,4224,3351],{"class":541},[528,4226,4227],{"class":530,"line":1174},[528,4228,703],{"class":541},[448,4230,4231,859,4233,598],{},[452,4232,799],{},[460,4234,4235,495,4237,809,4240,495,4242],{"href":94},[480,4236,4014],{},[480,4238,4239],{},"CheckPermission",[480,4241,4033],{},[480,4243,4244],{},"RequirePermission",[448,4246,4247,859,4249,4254],{},[452,4248,858],{},[460,4250,4253],{"href":4251,"rel":4252},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Fcodewalk\u002Ffunctions\u002F",[864],"Go blog: Function values"," (magasabb-rendű függvények Go-ban).",[469,4256,4258],{"id":4257},"fail-closed-policy-enforcement","Fail-closed policy enforcement",[448,4260,4261,885,4263,495,4266,4269,4270,4273],{},[452,4262,477],{},[480,4264,4265],{},"@permission",[480,4267,4268],{},"@policy"," TypeSpec-dekorátorokból generált enforcer middleware ",[452,4271,4272],{},"elutasít",", ha egy megjelölt művelethez nincs regisztrált policy a registry-ben: a hiányzó konfiguráció hiba, nem hallgatólagos átengedés.",[448,4275,4276,859,4278,4283,4284,598],{},[452,4277,799],{},[460,4279,4280],{"href":99},[480,4281,4282],{},"policy.Enforcer",", a generálási lánc: ",[460,4285,233],{"href":4286},"\u002Fhu\u002Fconcepts\u002Fcodegen-pipeline#deklarat%C3%ADv-authoriz%C3%A1ci%C3%B3-permission-%C3%A9s-policy",[448,4288,4289,859,4291,4296],{},[452,4290,858],{},[460,4292,4295],{"href":4293,"rel":4294},"https:\u002F\u002Fowasp.org\u002Fwww-community\u002FFail_securely",[864],"OWASP: Fail securely"," alapelv.",[464,4298,4300],{"id":4299},"codegen-és-tooling","Codegen és tooling",[469,4302,4304],{"id":4303},"anchor-comment-kód-injektálás","Anchor-comment kód-injektálás",[448,4306,4307,4309,4310,4313],{},[452,4308,477],{}," a generátorok soha nem írnak felül meglévő fájlt: kizárólag egy ",[480,4311,4312],{},"\u002F\u002F gpsystem:\u003Cnév>"," jelölésű sor elé illesztenek be új kódot, az anchor sort megtartva, hogy a következő futás is odataláljon. Az idempotenciát egy whitespace-normalizált összevetés biztosítja: ha a gofmt utólag újraigazította a beillesztett blokkot, a generátor a normalizált formában is felismeri, és nem duplikál.",[519,4315,4317],{"className":521,"code":4316,"language":523,"meta":524,"style":524},"\u002F\u002F internal\u002Fgenerator\u002Fanchors.go\nfunc normalizeLine(s string) string { return strings.Join(strings.Fields(s), \" \") }\n\nfunc InsertIntoContent(content, anchor, snippet string) (updated string, inserted bool, err error) {\n    \u002F\u002F ... anchor sor megkeresése, indentáció átvétele\n    if containsNormalizedBlock(content, block) {\n        return content, false, nil \u002F\u002F már bent van, nincs duplikáció\n    }\n    \u002F\u002F ... beillesztés az anchor elé\n}\n",[480,4318,4319,4324,4379,4383,4433,4438,4458,4477,4481,4486],{"__ignoreMap":524},[528,4320,4321],{"class":530,"line":531},[528,4322,4323],{"class":534},"\u002F\u002F internal\u002Fgenerator\u002Fanchors.go\n",[528,4325,4326,4328,4331,4333,4336,4338,4340,4342,4344,4346,4349,4351,4354,4356,4359,4361,4364,4366,4368,4370,4373,4375,4377],{"class":530,"line":538},[528,4327,568],{"class":541},[528,4329,4330],{"class":571}," normalizeLine",[528,4332,575],{"class":541},[528,4334,4335],{"class":578},"s",[528,4337,583],{"class":582},[528,4339,604],{"class":541},[528,4341,583],{"class":582},[528,4343,636],{"class":541},[528,4345,2210],{"class":620},[528,4347,4348],{"class":639}," strings",[528,4350,598],{"class":541},[528,4352,4353],{"class":571},"Join",[528,4355,575],{"class":541},[528,4357,4358],{"class":639},"strings",[528,4360,598],{"class":541},[528,4362,4363],{"class":571},"Fields",[528,4365,575],{"class":541},[528,4367,4335],{"class":639},[528,4369,3960],{"class":541},[528,4371,4372],{"class":541}," \"",[528,4374,4372],{"class":541},[528,4376,604],{"class":541},[528,4378,697],{"class":541},[528,4380,4381],{"class":530,"line":558},[528,4382,562],{"emptyLinePlaceholder":561},[528,4384,4385,4387,4390,4392,4395,4397,4400,4402,4405,4407,4409,4411,4414,4416,4418,4421,4423,4425,4427,4429,4431],{"class":530,"line":565},[528,4386,568],{"class":541},[528,4388,4389],{"class":571}," InsertIntoContent",[528,4391,575],{"class":541},[528,4393,4394],{"class":578},"content",[528,4396,586],{"class":541},[528,4398,4399],{"class":578}," anchor",[528,4401,586],{"class":541},[528,4403,4404],{"class":578}," snippet",[528,4406,583],{"class":582},[528,4408,604],{"class":541},[528,4410,822],{"class":541},[528,4412,4413],{"class":578},"updated",[528,4415,583],{"class":582},[528,4417,586],{"class":541},[528,4419,4420],{"class":578}," inserted",[528,4422,2475],{"class":582},[528,4424,586],{"class":541},[528,4426,2687],{"class":578},[528,4428,607],{"class":582},[528,4430,604],{"class":541},[528,4432,614],{"class":541},[528,4434,4435],{"class":530,"line":617},[528,4436,4437],{"class":534},"    \u002F\u002F ... anchor sor megkeresése, indentáció átvétele\n",[528,4439,4440,4442,4445,4447,4449,4451,4454,4456],{"class":530,"line":700},[528,4441,2715],{"class":620},[528,4443,4444],{"class":571}," containsNormalizedBlock",[528,4446,575],{"class":541},[528,4448,4394],{"class":639},[528,4450,586],{"class":541},[528,4452,4453],{"class":639}," block",[528,4455,604],{"class":541},[528,4457,614],{"class":541},[528,4459,4460,4462,4465,4467,4470,4472,4474],{"class":530,"line":706},[528,4461,3125],{"class":620},[528,4463,4464],{"class":639}," content",[528,4466,586],{"class":541},[528,4468,4469],{"class":3185}," false",[528,4471,586],{"class":541},[528,4473,2522],{"class":541},[528,4475,4476],{"class":534}," \u002F\u002F már bent van, nincs duplikáció\n",[528,4478,4479],{"class":530,"line":745},[528,4480,2753],{"class":541},[528,4482,4483],{"class":530,"line":792},[528,4484,4485],{"class":534},"    \u002F\u002F ... beillesztés az anchor elé\n",[528,4487,4488],{"class":530,"line":1105},[528,4489,703],{"class":541},[448,4491,4492,859,4494,4502],{},[452,4493,799],{},[460,4495,4497,4498,4501],{"href":4496},"\u002Fhu\u002Fconcepts\u002Fcodegen-pipeline#a-gpsystem-anchorok","Codegen pipeline: a ",[480,4499,4500],{},"\u002F\u002F gpsystem:*"," anchorok",": a teljes anchor-lista és mit illeszt hozzájuk melyik parancs.",[448,4504,4505,4507],{},[452,4506,858],{}," a minta a scaffolding-generátorok más ökoszisztémáiban is elterjedt \"marker comment\" technika rokona; Go-specifikus előzmény nincs, a kit saját megoldása.",[469,4509,4511],{"id":4510},"plan-then-apply-generátor-írás","Plan-then-apply generátor-írás",[448,4513,4514,4516,4517,4520,4521,4524,4525,4528],{},[452,4515,477],{}," a fájlgenerátor előbb ",[452,4518,4519],{},"teljes egészében kiszámolja",", mely fájlok ütköznének meglévőkkel, és csak ez után ír bármit: ",[480,4522,4523],{},"--force"," nélkül egyetlen ütközés is az egész futást megállítja, mielőtt egy byte is a lemezre kerülne. ",[480,4526,4527],{},"--dry-run"," mellett a terv csak kiíródik, semmi nem történik.",[519,4530,4532],{"className":521,"code":4531,"language":523,"meta":524,"style":524},"\u002F\u002F internal\u002Fgenerator\u002Fengine.go: Apply\nvar conflicts []string\nfor _, f := range files {\n    if _, err := os.Stat(f.Path); err == nil {\n        conflicts = append(conflicts, f.Path)\n    }\n}\nif len(conflicts) > 0 && !e.Force {\n    return fmt.Errorf(\"refusing to overwrite existing files (use --force):\\n  %s\", ...)\n}\n",[480,4533,4534,4539,4552,4572,4611,4636,4640,4644,4677,4710],{"__ignoreMap":524},[528,4535,4536],{"class":530,"line":531},[528,4537,4538],{"class":534},"\u002F\u002F internal\u002Fgenerator\u002Fengine.go: Apply\n",[528,4540,4541,4543,4546,4549],{"class":530,"line":538},[528,4542,2159],{"class":541},[528,4544,4545],{"class":639}," conflicts ",[528,4547,4548],{"class":541},"[]",[528,4550,4551],{"class":582},"string\n",[528,4553,4554,4556,4558,4560,4563,4565,4567,4570],{"class":530,"line":558},[528,4555,1149],{"class":620},[528,4557,1152],{"class":639},[528,4559,586],{"class":541},[528,4561,4562],{"class":639}," f ",[528,4564,1160],{"class":541},[528,4566,1163],{"class":620},[528,4568,4569],{"class":639}," files ",[528,4571,950],{"class":541},[528,4573,4574,4576,4578,4580,4582,4584,4587,4589,4592,4594,4596,4598,4601,4603,4605,4607,4609],{"class":530,"line":565},[528,4575,2715],{"class":620},[528,4577,1152],{"class":639},[528,4579,586],{"class":541},[528,4581,3150],{"class":639},[528,4583,1160],{"class":541},[528,4585,4586],{"class":639}," os",[528,4588,598],{"class":541},[528,4590,4591],{"class":571},"Stat",[528,4593,575],{"class":541},[528,4595,1335],{"class":639},[528,4597,598],{"class":541},[528,4599,4600],{"class":639},"Path",[528,4602,3116],{"class":541},[528,4604,3150],{"class":639},[528,4606,2534],{"class":541},[528,4608,2522],{"class":541},[528,4610,614],{"class":541},[528,4612,4613,4616,4618,4620,4622,4625,4627,4630,4632,4634],{"class":530,"line":617},[528,4614,4615],{"class":639},"        conflicts ",[528,4617,648],{"class":541},[528,4619,651],{"class":571},[528,4621,575],{"class":541},[528,4623,4624],{"class":639},"conflicts",[528,4626,586],{"class":541},[528,4628,4629],{"class":639}," f",[528,4631,598],{"class":541},[528,4633,4600],{"class":639},[528,4635,555],{"class":541},[528,4637,4638],{"class":530,"line":700},[528,4639,2753],{"class":541},[528,4641,4642],{"class":530,"line":706},[528,4643,703],{"class":541},[528,4645,4646,4648,4651,4653,4655,4657,4660,4663,4665,4667,4670,4672,4675],{"class":530,"line":745},[528,4647,896],{"class":620},[528,4649,4650],{"class":571}," len",[528,4652,575],{"class":541},[528,4654,4624],{"class":639},[528,4656,604],{"class":541},[528,4658,4659],{"class":541}," >",[528,4661,4662],{"class":2612}," 0",[528,4664,2525],{"class":541},[528,4666,940],{"class":541},[528,4668,4669],{"class":639},"e",[528,4671,598],{"class":541},[528,4673,4674],{"class":639},"Force ",[528,4676,950],{"class":541},[528,4678,4679,4681,4684,4686,4689,4691,4693,4696,4699,4703,4705,4707],{"class":530,"line":792},[528,4680,621],{"class":620},[528,4682,4683],{"class":639}," fmt",[528,4685,598],{"class":541},[528,4687,4688],{"class":571},"Errorf",[528,4690,575],{"class":541},[528,4692,3493],{"class":541},[528,4694,4695],{"class":2002},"refusing to overwrite existing files (use --force):",[528,4697,4698],{"class":639},"\\n",[528,4700,4702],{"class":4701},"swJcz","  %s",[528,4704,3493],{"class":541},[528,4706,586],{"class":541},[528,4708,4709],{"class":541}," ...)\n",[528,4711,4712],{"class":530,"line":1105},[528,4713,703],{"class":541},[448,4715,4716,859,4718,4723,4724,495,4727,4730],{},[452,4717,799],{},[460,4719,4720],{"href":287},[480,4721,4722],{},"internal\u002Fgenerator.Engine.Apply",": minden ",[480,4725,4726],{},"new",[480,4728,4729],{},"add"," parancs ezen keresztül ír.",[448,4732,4733,4735,4736,495,4739,4742,4743,598],{},[452,4734,858],{}," a minta rokona a Terraform ",[480,4737,4738],{},"plan",[480,4740,4741],{},"apply"," kettősségének: ",[460,4744,4747],{"href":4745,"rel":4746},"https:\u002F\u002Fdeveloper.hashicorp.com\u002Fterraform\u002Fcli\u002Fcommands\u002Fplan",[864],"terraform.io: Plan",[469,4749,4751],{"id":4750},"spec-first-codegen","Spec-first codegen",[448,4753,4754,4756],{},[452,4755,477],{}," az API-kontraktus egyetlen forrásigazsága egy deklaratív specifikáció (TypeSpec), amiből determinisztikusan fordul OpenAPI, abból pedig típusos, \"strict\" szerver-interfész: egy spec-eltérés fordítási hiba, nem futásidejű meglepetés.",[448,4758,4759,859,4761,4763,4764,4767],{},[452,4760,799],{},[460,4762,233],{"href":234},": a teljes ",[480,4765,4766],{},"TypeSpec → OpenAPI 3.0 → oapi-codegen"," lánc, engine-enkénti template-készlettel.",[448,4769,4770,859,4772,809,4777,598],{},[452,4771,858],{},[460,4773,4776],{"href":4774,"rel":4775},"https:\u002F\u002Ftypespec.io\u002Fdocs",[864],"typespec.io: What is TypeSpec",[460,4778,4781],{"href":4779,"rel":4780},"https:\u002F\u002Fgithub.com\u002Foapi-codegen\u002Foapi-codegen",[864],"oapi-codegen",[464,4783,4785],{"id":4784},"architekturális-alapelvek","Architekturális alapelvek",[448,4787,4788,4789,4791],{},"Két minta nem egy csomaghoz köthető, hanem a kit egészének felépítését magyarázza, ezeket az ",[460,4790,220],{"href":221}," oldal tárgyalja részletesen, itt csak a nevük és a linkjük:",[4793,4794,4795,4804],"ul",{},[4796,4797,4798,4803],"li",{},[452,4799,4800],{},[460,4801,4802],{"href":1882},"\"Centralizál, nem absztrahál\"",": a 3rd-party függőségekhez (chi, pgx, bun, asynq) való hozzáférés egy-egy csomagba koncentrálódik, de a típusaik szabadon átfolynak a publikus API-n; nincs kit-specifikus wrapper-absztrakció megtanulandó.",[4796,4805,4806,4809,4810,809,4815,809,4819,4824,4825,4828,4829,4832],{},[452,4807,4808],{},"Interfész-szegregáció \u002F dependency inversion a három kimondott kivételnél",": ",[460,4811,4812],{"href":153},[480,4813,4814],{},"storage.Store",[460,4816,4817],{"href":194},[480,4818,1080],{},[460,4820,4821],{"href":119},[480,4822,4823],{},"mail.Mailer",", ahol a kit mégis interfész mögé rejt egy függőséget, ott ez mindig az ",[452,4826,4827],{},"elhagyhatóság"," vagy ",[452,4830,4831],{},"cserélhetőség"," miatt van, dokumentált indokkal.",[464,4834,4836],{"id":4835},"merre-tovább","Merre tovább",[4793,4838,4839,4845,4851],{},[4796,4840,4841,4842,4844],{},"A ",[460,4843,209],{"href":210}," oldal minden mögöttes library-t felsorol verzióval és hivatalos dokumentációval.",[4796,4846,4847,4848,4850],{},"Az ",[460,4849,220],{"href":221}," a csomagtérképet és a tervezési alapelvet adja, aminek ez az oldal a minta-szintű bontása.",[4796,4852,4841,4853,3707,4855,4857],{},[460,4854,224],{"href":225},[460,4856,233],{"href":234}," a két legrészletesebben tárgyalt minta-csoportot (errs\u002Fhttperr, illetve anchor-injektálás\u002Fspec-first codegen) teljes hosszában kifejtik.",[4859,4860,4861],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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);}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":524,"searchDepth":538,"depth":538,"links":4863},[4864,4871,4879,4884,4889,4894,4899,4900],{"id":466,"depth":538,"text":467,"children":4865},[4866,4867,4868,4869,4870],{"id":471,"depth":558,"text":472},{"id":900,"depth":558,"text":901},{"id":1213,"depth":558,"text":1214},{"id":1392,"depth":558,"text":1393},{"id":1612,"depth":558,"text":1613},{"id":1941,"depth":538,"text":1942,"children":4872},[4873,4874,4876,4878],{"id":1945,"depth":558,"text":1946},{"id":2123,"depth":558,"text":4875},"Kaszkádoló errors.As mapping",{"id":2370,"depth":558,"text":4877},"Sentinel-definíció testreszabott Is-sel",{"id":2571,"depth":558,"text":2572},{"id":2793,"depth":538,"text":2794,"children":4880},[4881,4882,4883],{"id":2797,"depth":558,"text":2798},{"id":3015,"depth":558,"text":3016},{"id":3395,"depth":558,"text":3396},{"id":3540,"depth":538,"text":3541,"children":4885},[4886,4887,4888],{"id":3544,"depth":558,"text":3545},{"id":3662,"depth":558,"text":3663},{"id":3727,"depth":558,"text":3728},{"id":3827,"depth":538,"text":3828,"children":4890},[4891,4892,4893],{"id":3831,"depth":558,"text":3832},{"id":4005,"depth":558,"text":4006},{"id":4257,"depth":558,"text":4258},{"id":4299,"depth":538,"text":4300,"children":4895},[4896,4897,4898],{"id":4303,"depth":558,"text":4304},{"id":4510,"depth":558,"text":4511},{"id":4750,"depth":558,"text":4751},{"id":4784,"depth":538,"text":4785},{"id":4835,"depth":538,"text":4836},"A kitben visszatérő tervezési minták katalógusa: kódpéldával, előfordulási hellyel és a kanonikus külső forrással.","md",null,{},{"icon":241},{"title":238,"description":4901},"ktWBJcfYtKi_gUs7oEQs3i62hC5aOHZbpBVngVET0bA",[4909,4911],{"title":233,"path":234,"stem":235,"description":4910,"icon":236,"children":-1},"TypeSpec → OpenAPI 3.0 → szigorú, típusos szerverkód chi\u002Fnet-http-n.",{"title":249,"path":250,"stem":251,"description":4912,"icon":244,"children":-1},"Mi maradt a vékony gpsystem kitben, és hogyan komponálja az önálló modulokat.",1785445886541]