[{"data":1,"prerenderedAt":554},["ShallowReactive",2],{"docs-nav:en":3,"docs:\u002Fdocs\u002Fconcepts\u002Fshortest-path":134},[4],{"title":5,"path":6,"stem":7,"children":8},"Docs","\u002Fdocs","docs",[9,12,16,20,24,42,63,92,113],{"title":10,"path":6,"stem":11},"What is Fougere","docs\u002Findex",{"title":13,"path":14,"stem":15},"Getting started","\u002Fdocs\u002Fgetting-started","docs\u002F01.getting-started",{"title":17,"path":18,"stem":19},"Existing Nuxt app","\u002Fdocs\u002Fexisting-app","docs\u002F02.existing-app",{"title":21,"path":22,"stem":23},"The CLI","\u002Fdocs\u002Fcli","docs\u002F03.cli",{"title":25,"path":26,"stem":27,"children":28,"page":41},"Concepts","\u002Fdocs\u002Fconcepts","docs\u002F2.concepts",[29,33,37],{"title":30,"path":31,"stem":32},"Philosophy","\u002Fdocs\u002Fconcepts\u002Fphilosophy","docs\u002F2.concepts\u002F1.philosophy",{"title":34,"path":35,"stem":36},"The Frond","\u002Fdocs\u002Fconcepts\u002Ffrond","docs\u002F2.concepts\u002F2.frond",{"title":38,"path":39,"stem":40},"The shortest path","\u002Fdocs\u002Fconcepts\u002Fshortest-path","docs\u002F2.concepts\u002F3.shortest-path",false,{"title":43,"path":44,"stem":45,"children":46,"page":41},"Schema","\u002Fdocs\u002Fschema","docs\u002F3.schema",[47,51,55,59],{"title":48,"path":49,"stem":50},"Entities","\u002Fdocs\u002Fschema\u002Fentities","docs\u002F3.schema\u002F1.entities",{"title":52,"path":53,"stem":54},"Views","\u002Fdocs\u002Fschema\u002Fviews","docs\u002F3.schema\u002F2.views",{"title":56,"path":57,"stem":58},"The identity card","\u002Fdocs\u002Fschema\u002Fcard","docs\u002F3.schema\u002F3.card",{"title":60,"path":61,"stem":62},"Standard Schema","\u002Fdocs\u002Fschema\u002Fstandard-schema","docs\u002F3.schema\u002F4.standard-schema",{"title":64,"path":65,"stem":66,"children":67,"page":41},"Business","\u002Fdocs\u002Fbusiness","docs\u002F4.business",[68,72,76,80,84,88],{"title":69,"path":70,"stem":71},"Handlers","\u002Fdocs\u002Fbusiness\u002Fhandlers","docs\u002F4.business\u002F1.handlers",{"title":73,"path":74,"stem":75},"Presenters","\u002Fdocs\u002Fbusiness\u002Fpresenters","docs\u002F4.business\u002F2.presenters",{"title":77,"path":78,"stem":79},"Collectors","\u002Fdocs\u002Fbusiness\u002Fcollectors","docs\u002F4.business\u002F3.collectors",{"title":81,"path":82,"stem":83},"Errors","\u002Fdocs\u002Fbusiness\u002Ferrors","docs\u002F4.business\u002F4.errors",{"title":85,"path":86,"stem":87},"Seeds","\u002Fdocs\u002Fbusiness\u002Fseeds","docs\u002F4.business\u002F5.seeds",{"title":89,"path":90,"stem":91},"Facts","\u002Fdocs\u002Fbusiness\u002Ffacts","docs\u002F4.business\u002F6.facts",{"title":93,"path":94,"stem":95,"children":96,"page":41},"Client","\u002Fdocs\u002Fclient","docs\u002F5.client",[97,101,105,109],{"title":98,"path":99,"stem":100},"Queries & commands","\u002Fdocs\u002Fclient\u002Fqueries-commands","docs\u002F5.client\u002F1.queries-commands",{"title":102,"path":103,"stem":104},"Forms","\u002Fdocs\u002Fclient\u002Fforms","docs\u002F5.client\u002F2.forms",{"title":106,"path":107,"stem":108},"Session","\u002Fdocs\u002Fclient\u002Fsession","docs\u002F5.client\u002F3.session",{"title":110,"path":111,"stem":112},"invoke","\u002Fdocs\u002Fclient\u002Finvoke","docs\u002F5.client\u002F4.invoke",{"title":114,"path":115,"stem":116,"children":117,"page":41},"Infrastructure","\u002Fdocs\u002Finfra","docs\u002F6.infra",[118,122,126,130],{"title":119,"path":120,"stem":121},"The gradient","\u002Fdocs\u002Finfra\u002Fgradient","docs\u002F6.infra\u002F1.gradient",{"title":123,"path":124,"stem":125},"Surfaces — REST & GraphQL","\u002Fdocs\u002Finfra\u002Fsurfaces","docs\u002F6.infra\u002F2.surfaces",{"title":127,"path":128,"stem":129},"Deployment","\u002Fdocs\u002Finfra\u002Fdeployment","docs\u002F6.infra\u002F3.deployment",{"title":131,"path":132,"stem":133},"Hosts","\u002Fdocs\u002Finfra\u002Fhosts","docs\u002F6.infra\u002F4.hosts",{"id":135,"title":38,"body":136,"description":549,"extension":550,"meta":551,"navigation":462,"path":39,"seo":552,"stem":40,"__hash__":553},"docs_en\u002Fdocs\u002F2.concepts\u002F3.shortest-path.md",{"type":137,"value":138,"toc":542},"minimark",[139,143,147,155,160,163,243,246,250,253,256,260,263,285,293,296,319,322,326,333,519,526,530,533,538],[140,141,38],"h1",{"id":142},"the-shortest-path",[144,145,146],"p",{},"When a task is urgent, the simplest available path will usually be used. Fougere\ntherefore aims to make the conventional path shorter than bypassing it.",[144,148,149,150,154],{},"This page covers a design principle and its related tooling. ",[151,152,153],"code",{},"fougere graph"," is available\ntoday. CI graph checks and the refusal catalog described below are still under study.",[156,157,159],"h2",{"id":158},"one-api-for-each-common-need","One API for each common need",[144,161,162],{},"Common operations have a recommended API:",[164,165,166,179],"table",{},[167,168,169],"thead",{},[170,171,172,176],"tr",{},[173,174,175],"th",{},"Need",[173,177,178],{},"API",[180,181,182,196,208,220,231],"tbody",{},[170,183,184,188],{},[185,186,187],"td",{},"read from a page",[185,189,190],{},[191,192,193],"a",{"href":99},[151,194,195],{},"useQuery",[170,197,198,201],{},[185,199,200],{},"change state from a page",[185,202,203],{},[191,204,205],{"href":99},[151,206,207],{},"useCommand",[170,209,210,213],{},[185,211,212],{},"manage a form",[185,214,215],{},[191,216,217],{"href":103},[151,218,219],{},"useFormFor",[170,221,222,225],{},[185,223,224],{},"call from the server",[185,226,227],{},[191,228,229],{"href":111},[151,230,110],{},[170,232,233,236],{},[185,234,235],{},"validate input",[185,237,238],{},[191,239,240],{"href":49},[151,241,242],{},"Entity.validate",[144,244,245],{},"These APIs share the same entity-and-operation designation. They also pass the context\nexpected by collectors and normalize errors. A manually assembled call must handle those\nresponsibilities explicitly.",[156,247,249],{"id":248},"how-a-codebase-drifts","How a codebase drifts",[144,251,252],{},"Architecture drift often comes from reasonable local fixes: a rule added to a form, then\nto a route, then to an adapter. Each change solves its immediate problem, but the rule no\nlonger has one owner.",[144,254,255],{},"Fougere conventions place field rules in entities and business rules in operations. They\ndo not guarantee that the model is correct, but they give elements that must change\ntogether a shared location.",[156,257,259],{"id":258},"read-the-graph","Read the graph",[144,261,262],{},"The scanner knows about Fronds, their entities, and their references. The following\ncommand prints those relationships:",[264,265,270],"pre",{"className":266,"code":267,"language":268,"meta":269,"style":269},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","fougere graph\n","bash","",[151,271,272],{"__ignoreMap":269},[273,274,277,281],"span",{"class":275,"line":276},"line",1,[273,278,280],{"class":279},"sbgvK","fougere",[273,282,284],{"class":283},"s_sjI"," graph\n",[264,286,291],{"className":287,"code":289,"language":290,"meta":269},[288],"language-text","Post -> Author, Category (2 incoming)\nAuthor\n","text",[151,292,289],{"__ignoreMap":269},[144,294,295],{},"This graph can be read along three dimensions:",[297,298,299,307,313],"ul",{},[300,301,302,306],"li",{},[303,304,305],"strong",{},"coupling",": the number of relationships attached to an entity;",[300,308,309,312],{},[303,310,311],{},"duplication",": locations where the same rule is declared;",[300,314,315,318],{},[303,316,317],{},"ownership",": the domain responsible for a rule or piece of data.",[144,320,321],{},"The current command exposes relationships and suggests clusters beyond six entities. It\ndoes not yet measure duplication or ownership automatically. Showing graph changes in\npull requests remains a possible CI feature.",[156,323,325],{"id":324},"errors-that-suggest-a-solution","Errors that suggest a solution",[144,327,328,329,332],{},"A useful safeguard should identify the detected usage, its risk, and the recommended API.\nThe following shape illustrates the planned catalog; ",[151,330,331],{},"defineRefusal"," is not a shipped API:",[264,334,338],{"className":335,"code":336,"language":337,"meta":269,"style":269},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","defineRefusal({\n  id: 'F-042',\n  detect: (site) => isHandRolledCallToFrond(site),\n  message: (site) => dedent`\n    Hand-assembled call to the ${site.frond} Frond.\n    This call does not automatically pass identity and locale.\n\n      -> invoke('${site.suggestion}')\n  `,\n  origin: 'incident: context omitted from several routes',\n});\n","ts",[151,339,340,353,376,406,428,451,457,464,484,492,509],{"__ignoreMap":269},[273,341,342,345,349],{"class":275,"line":276},[273,343,331],{"class":344},"sGLFI",[273,346,348],{"class":347},"su5hD","(",[273,350,352],{"class":351},"sP7_E","{\n",[273,354,356,360,363,367,370,373],{"class":275,"line":355},2,[273,357,359],{"class":358},"skxfh","  id",[273,361,362],{"class":351},":",[273,364,366],{"class":365},"sjJ54"," '",[273,368,369],{"class":283},"F-042",[273,371,372],{"class":365},"'",[273,374,375],{"class":351},",\n",[273,377,379,382,384,387,391,394,398,401,404],{"class":275,"line":378},3,[273,380,381],{"class":344},"  detect",[273,383,362],{"class":351},[273,385,386],{"class":351}," (",[273,388,390],{"class":389},"s99_P","site",[273,392,393],{"class":351},")",[273,395,397],{"class":396},"sbsja"," =>",[273,399,400],{"class":344}," isHandRolledCallToFrond",[273,402,403],{"class":347},"(site)",[273,405,375],{"class":351},[273,407,409,412,414,416,418,420,422,425],{"class":275,"line":408},4,[273,410,411],{"class":344},"  message",[273,413,362],{"class":351},[273,415,386],{"class":351},[273,417,390],{"class":389},[273,419,393],{"class":351},[273,421,397],{"class":396},[273,423,424],{"class":344}," dedent",[273,426,427],{"class":365},"`\n",[273,429,431,434,437,439,442,445,448],{"class":275,"line":430},5,[273,432,433],{"class":283},"    Hand-assembled call to the ",[273,435,436],{"class":365},"${",[273,438,390],{"class":347},[273,440,441],{"class":365},".",[273,443,444],{"class":347},"frond",[273,446,447],{"class":365},"}",[273,449,450],{"class":283}," Frond.\n",[273,452,454],{"class":275,"line":453},6,[273,455,456],{"class":283},"    This call does not automatically pass identity and locale.\n",[273,458,460],{"class":275,"line":459},7,[273,461,463],{"emptyLinePlaceholder":462},true,"\n",[273,465,467,470,472,474,476,479,481],{"class":275,"line":466},8,[273,468,469],{"class":283},"      -> invoke('",[273,471,436],{"class":365},[273,473,390],{"class":347},[273,475,441],{"class":365},[273,477,478],{"class":347},"suggestion",[273,480,447],{"class":365},[273,482,483],{"class":283},"')\n",[273,485,487,490],{"class":275,"line":486},9,[273,488,489],{"class":365},"  `",[273,491,375],{"class":351},[273,493,495,498,500,502,505,507],{"class":275,"line":494},10,[273,496,497],{"class":358},"  origin",[273,499,362],{"class":351},[273,501,366],{"class":365},[273,503,504],{"class":283},"incident: context omitted from several routes",[273,506,372],{"class":365},[273,508,375],{"class":351},[273,510,512,514,516],{"class":275,"line":511},11,[273,513,447],{"class":351},[273,515,393],{"class":347},[273,517,518],{"class":351},";\n",[144,520,521,522,525],{},"These build-time errors would remain separate from\n",[191,523,524],{"href":82},"business errors"," returned at runtime.",[156,527,529],{"id":528},"limit","Limit",[144,531,532],{},"Fougere can check the structure of a declaration, not whether it is correct for the\nbusiness. An incorrect rule remains incorrect when declared only once. Choosing concepts\nand their boundaries remains a design decision.",[144,534,535,536,441],{},"Next: ",[191,537,48],{"href":49},[539,540,541],"style",{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":269,"searchDepth":355,"depth":355,"links":543},[544,545,546,547,548],{"id":158,"depth":355,"text":159},{"id":248,"depth":355,"text":249},{"id":258,"depth":355,"text":259},{"id":324,"depth":355,"text":325},{"id":528,"depth":355,"text":529},"Fougere conventions, the available graph, and safeguards still under study.","md",{},{"title":38,"description":549},"1zybkn8vIzkIJb2m7-ck3ZenL864SL-s0hQUV5saZUc",1786637819526]