[{"data":1,"prerenderedAt":890},["ShallowReactive",2],{"navigation":3,"\u002Fguides\u002Ftesting-and-ci":193,"\u002Fguides\u002Ftesting-and-ci-surround":885},[4,27,49,99,149,169,183],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":26},"Getting Started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,14,18,22],{"title":11,"path":12,"stem":13},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction",{"title":15,"path":16,"stem":17},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation",{"title":19,"path":20,"stem":21},"Quick Start","\u002Fgetting-started\u002Fquick-start","1.getting-started\u002F3.quick-start",{"title":23,"path":24,"stem":25},"Existing Application","\u002Fgetting-started\u002Fexisting-application","1.getting-started\u002F4.existing-application",false,{"title":28,"icon":29,"path":30,"stem":31,"children":32,"page":26},"Concepts","i-lucide-layers","\u002Fconcepts","2.concepts",[33,37,41,45],{"title":34,"path":35,"stem":36},"Layers","\u002Fconcepts\u002Flayers","2.concepts\u002F1.layers",{"title":38,"path":39,"stem":40},"Buckets","\u002Fconcepts\u002Fbuckets","2.concepts\u002F2.buckets",{"title":42,"path":43,"stem":44},"Boundaries","\u002Fconcepts\u002Fboundaries","2.concepts\u002F3.boundaries",{"title":46,"path":47,"stem":48},"Service Providers","\u002Fconcepts\u002Fservice-providers","2.concepts\u002F4.service-providers",{"title":50,"icon":51,"path":52,"stem":53,"children":54,"page":26},"Guides","i-lucide-book-marked","\u002Fguides","3.guides",[55,59,63,67,71,75,79,83,87,91,95],{"title":56,"path":57,"stem":58},"Add a Domain","\u002Fguides\u002Fadd-a-domain","3.guides\u002F01.add-a-domain",{"title":60,"path":61,"stem":62},"Share Code Between Layers","\u002Fguides\u002Fshare-code-between-layers","3.guides\u002F02.share-code-between-layers",{"title":64,"path":65,"stem":66},"Routes","\u002Fguides\u002Froutes","3.guides\u002F03.routes",{"title":68,"path":69,"stem":70},"Views and Components","\u002Fguides\u002Fviews-and-components","3.guides\u002F04.views-and-components",{"title":72,"path":73,"stem":74},"Seeders","\u002Fguides\u002Fseeders","3.guides\u002F05.seeders",{"title":76,"path":77,"stem":78},"Configuration","\u002Fguides\u002Fconfiguration","3.guides\u002F06.configuration",{"title":80,"path":81,"stem":82},"Events","\u002Fguides\u002Fevents","3.guides\u002F07.events",{"title":84,"path":85,"stem":86},"Tests and CI","\u002Fguides\u002Ftesting-and-ci","3.guides\u002F08.testing-and-ci",{"title":88,"path":89,"stem":90},"Production","\u002Fguides\u002Fproduction","3.guides\u002F09.production",{"title":92,"path":93,"stem":94},"Remove a Layer","\u002Fguides\u002Fremoving-a-layer","3.guides\u002F10.removing-a-layer",{"title":96,"path":97,"stem":98},"Multi-tenancy","\u002Fguides\u002Fmulti-tenancy","3.guides\u002F11.multi-tenancy",{"title":100,"icon":101,"path":102,"stem":103,"children":104,"page":26},"Commands","i-lucide-terminal","\u002Fcommands","4.commands",[105,109,113,117,121,125,129,133,137,141,145],{"title":106,"path":107,"stem":108},"Overview","\u002Fcommands\u002Foverview","4.commands\u002F01.overview",{"title":110,"path":111,"stem":112},"osdd:start","\u002Fcommands\u002Fosdd-start","4.commands\u002F02.osdd-start",{"title":114,"path":115,"stem":116},"osdd:layer","\u002Fcommands\u002Fosdd-layer","4.commands\u002F03.osdd-layer",{"title":118,"path":119,"stem":120},"osdd:list","\u002Fcommands\u002Fosdd-list","4.commands\u002F04.osdd-list",{"title":122,"path":123,"stem":124},"osdd:check","\u002Fcommands\u002Fosdd-check","4.commands\u002F05.osdd-check",{"title":126,"path":127,"stem":128},"osdd:graph","\u002Fcommands\u002Fosdd-graph","4.commands\u002F06.osdd-graph",{"title":130,"path":131,"stem":132},"osdd:seed","\u002Fcommands\u002Fosdd-seed","4.commands\u002F07.osdd-seed",{"title":134,"path":135,"stem":136},"osdd:phpunit","\u002Fcommands\u002Fosdd-phpunit","4.commands\u002F08.osdd-phpunit",{"title":138,"path":139,"stem":140},"osdd:remove","\u002Fcommands\u002Fosdd-remove","4.commands\u002F09.osdd-remove",{"title":142,"path":143,"stem":144},"osdd:cache","\u002Fcommands\u002Fosdd-cache","4.commands\u002F10.osdd-cache",{"title":146,"path":147,"stem":148},"Generators","\u002Fcommands\u002Fmake-commands","4.commands\u002F11.make-commands",{"title":150,"icon":151,"path":152,"stem":153,"children":154,"page":26},"API Reference","i-lucide-book-open","\u002Fapi-reference","5.api-reference",[155,159,162,166],{"title":156,"path":157,"stem":158},"LayerServiceProvider","\u002Fapi-reference\u002Flayer-service-provider","5.api-reference\u002F1.layer-service-provider",{"title":34,"path":160,"stem":161},"\u002Fapi-reference\u002Flayers","5.api-reference\u002F2.layers",{"title":163,"path":164,"stem":165},"SeederRegistry","\u002Fapi-reference\u002Fseeder-registry","5.api-reference\u002F3.seeder-registry",{"title":76,"path":167,"stem":168},"\u002Fapi-reference\u002Fconfiguration","5.api-reference\u002F4.configuration",{"title":170,"icon":171,"path":172,"stem":173,"children":174,"page":26},"Integrations","i-lucide-puzzle","\u002Fintegrations","6.integrations",[175,179],{"title":176,"path":177,"stem":178},"Laravel Boost","\u002Fintegrations\u002Flaravel-boost","6.integrations\u002F1.laravel-boost",{"title":180,"path":181,"stem":182},"Tinker and IDE Helper","\u002Fintegrations\u002Ftinker-and-ide-helper","6.integrations\u002F2.tinker-and-ide-helper",{"title":184,"icon":185,"path":186,"stem":187,"children":188,"page":26},"Upgrade","i-lucide-arrow-up-circle","\u002Fupgrade","7.upgrade",[189],{"title":190,"path":191,"stem":192},"Upgrade from 2.x","\u002Fupgrade\u002Ffrom-2x","7.upgrade\u002F1.from-2x",{"id":194,"title":84,"body":195,"description":878,"extension":879,"links":880,"meta":881,"navigation":481,"path":85,"seo":882,"sitemap":883,"stem":86,"__hash__":884},"docs\u002F3.guides\u002F08.testing-and-ci.md",{"type":196,"value":197,"toc":872},"minimark",[198,213,218,266,310,328,332,346,363,395,399,419,577,581,595,841,849,865,868],[199,200,201,202,206,207,209,210,212],"p",{},"Each layer keeps its tests in its own ",[203,204,205],"code",{},"tests\u002F"," folder, under its own namespace. ",[203,208,134],{}," turns them into one PHPUnit suite per layer, and ",[203,211,122],{}," guards the boundaries in CI.",[214,215,217],"h2",{"id":216},"write-tests-in-a-layer","Write tests in a layer",[219,220,225],"pre",{"className":221,"code":222,"language":223,"meta":224,"style":224},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","php artisan osdd:test PlaceOrderTest --layer=functional\u002Forders\nphp artisan osdd:test OrderTotalTest --unit --layer=functional\u002Forders\n","bash","",[203,226,227,249],{"__ignoreMap":224},[228,229,232,236,240,243,246],"span",{"class":230,"line":231},"line",1,[228,233,235],{"class":234},"sBMFI","php",[228,237,239],{"class":238},"sfazB"," artisan",[228,241,242],{"class":238}," osdd:test",[228,244,245],{"class":238}," PlaceOrderTest",[228,247,248],{"class":238}," --layer=functional\u002Forders\n",[228,250,252,254,256,258,261,264],{"class":230,"line":251},2,[228,253,235],{"class":234},[228,255,239],{"class":238},[228,257,242],{"class":238},[228,259,260],{"class":238}," OrderTotalTest",[228,262,263],{"class":238}," --unit",[228,265,248],{"class":238},[267,268,269,282],"table",{},[270,271,272],"thead",{},[273,274,275,279],"tr",{},[276,277,278],"th",{},"Folder",[276,280,281],{},"Namespace",[283,284,285,298],"tbody",{},[273,286,287,293],{},[288,289,290],"td",{},[203,291,292],{},"functional\u002Forders\u002Ftests\u002FFeature",[288,294,295],{},[203,296,297],{},"Functional\\Orders\\Tests\\Feature",[273,299,300,305],{},[288,301,302],{},[203,303,304],{},"functional\u002Forders\u002Ftests\u002FUnit",[288,306,307],{},[203,308,309],{},"Functional\\Orders\\Tests\\Unit",[199,311,312,313,316,317,320,321,324,325,327],{},"A test may use the layers in the layer's ",[203,314,315],{},"require",", plus the ones in its ",[203,318,319],{},"require-dev",". Put test-only helpers in a layer such as ",[203,322,323],{},"technical\u002Ftesting"," and require it in ",[203,326,319],{},".",[214,329,331],{"id":330},"one-suite-per-layer","One suite per layer",[219,333,335],{"className":221,"code":334,"language":223,"meta":224,"style":224},"php artisan osdd:phpunit\n",[203,336,337],{"__ignoreMap":224},[228,338,339,341,343],{"class":230,"line":231},[228,340,235],{"class":234},[228,342,239],{"class":238},[228,344,345],{"class":238}," osdd:phpunit\n",[199,347,348,349,352,353,356,357,362],{},"The command adds a ",[203,350,351],{},"\u003Ctestsuite>"," named after each layer to ",[203,354,355],{},"phpunit.xml",", adds each layer's source directory to the coverage, and excludes the layer migrations from it. Run it again after creating a layer. See ",[358,359,360],"a",{"href":135},[203,361,134],{}," for the exact entries.",[219,364,366],{"className":221,"code":365,"language":223,"meta":224,"style":224},"php artisan test --testsuite=functional\u002Forders   # one layer\nphp artisan test                                 # everything\n",[203,367,368,384],{"__ignoreMap":224},[228,369,370,372,374,377,380],{"class":230,"line":231},[228,371,235],{"class":234},[228,373,239],{"class":238},[228,375,376],{"class":238}," test",[228,378,379],{"class":238}," --testsuite=functional\u002Forders",[228,381,383],{"class":382},"sHwdD","   # one layer\n",[228,385,386,388,390,392],{"class":230,"line":251},[228,387,235],{"class":234},[228,389,239],{"class":238},[228,391,376],{"class":238},[228,393,394],{"class":382},"                                 # everything\n",[214,396,398],{"id":397},"seed-in-tests","Seed in tests",[199,400,401,404,405,408,409,415,416,418],{},[203,402,403],{},"RefreshDatabase"," with ",[203,406,407],{},"$seed = true"," runs the layer seeders once ",[358,410,412],{"href":411},"\u002Fguides\u002Fseeders#seed-with-laravels-commands-and-in-tests",[203,413,414],{},"LayersSeeder"," is the default seeder, which ",[203,417,110],{}," sets up. Otherwise name it:",[219,420,423],{"className":421,"code":422,"language":235,"meta":224,"style":224},"language-php shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","use Illuminate\\Foundation\\Testing\\RefreshDatabase;\nuse Lomkit\\Osdd\\Seeders\\LayersSeeder;\n\nclass PlaceOrderTest extends TestCase\n{\n    use RefreshDatabase;\n\n    protected bool $seed = true;\n\n    protected string $seeder = LayersSeeder::class;\n}\n",[203,424,425,454,476,483,498,504,515,520,541,546,571],{"__ignoreMap":224},[228,426,427,431,435,439,442,444,447,449,451],{"class":230,"line":231},[228,428,430],{"class":429},"sbssI","use",[228,432,434],{"class":433},"sTEyZ"," Illuminate",[228,436,438],{"class":437},"sMK4o","\\",[228,440,441],{"class":433},"Foundation",[228,443,438],{"class":437},[228,445,446],{"class":433},"Testing",[228,448,438],{"class":437},[228,450,403],{"class":433},[228,452,453],{"class":437},";\n",[228,455,456,458,461,463,466,468,470,472,474],{"class":230,"line":251},[228,457,430],{"class":429},[228,459,460],{"class":433}," Lomkit",[228,462,438],{"class":437},[228,464,465],{"class":433},"Osdd",[228,467,438],{"class":437},[228,469,72],{"class":433},[228,471,438],{"class":437},[228,473,414],{"class":433},[228,475,453],{"class":437},[228,477,479],{"class":230,"line":478},3,[228,480,482],{"emptyLinePlaceholder":481},true,"\n",[228,484,486,490,492,495],{"class":230,"line":485},4,[228,487,489],{"class":488},"spNyl","class",[228,491,245],{"class":234},[228,493,494],{"class":488}," extends",[228,496,497],{"class":234}," TestCase\n",[228,499,501],{"class":230,"line":500},5,[228,502,503],{"class":437},"{\n",[228,505,507,510,513],{"class":230,"line":506},6,[228,508,509],{"class":429},"    use",[228,511,512],{"class":433}," RefreshDatabase",[228,514,453],{"class":437},[228,516,518],{"class":230,"line":517},7,[228,519,482],{"emptyLinePlaceholder":481},[228,521,523,526,529,532,535,538],{"class":230,"line":522},8,[228,524,525],{"class":488},"    protected",[228,527,528],{"class":429}," bool",[228,530,531],{"class":437}," $",[228,533,534],{"class":433},"seed ",[228,536,537],{"class":437},"=",[228,539,540],{"class":437}," true;\n",[228,542,544],{"class":230,"line":543},9,[228,545,482],{"emptyLinePlaceholder":481},[228,547,549,551,554,556,559,561,564,567,569],{"class":230,"line":548},10,[228,550,525],{"class":488},[228,552,553],{"class":429}," string",[228,555,531],{"class":437},[228,557,558],{"class":433},"seeder ",[228,560,537],{"class":437},[228,562,563],{"class":234}," LayersSeeder",[228,565,566],{"class":437},"::",[228,568,489],{"class":429},[228,570,453],{"class":437},[228,572,574],{"class":230,"line":573},11,[228,575,576],{"class":437},"}\n",[214,578,580],{"id":579},"check-the-boundaries-on-every-pull-request","Check the boundaries on every pull request",[199,582,583,584,586,587,590,591,594],{},"Run ",[203,585,122],{}," after ",[203,588,589],{},"composer install",", from the root of the Laravel project. With ",[203,592,593],{},"--format=github",", each violation becomes an annotation on the pull request's \"Files changed\" tab, and the step fails with exit code 1:",[219,596,601],{"className":597,"code":598,"filename":599,"language":600,"meta":224,"style":224},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","name: tests\n\non: [push, pull_request]\n\njobs:\n  tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n\n      - uses: shivammathur\u002Fsetup-php@v2\n        with:\n          php-version: '8.3'\n\n      - name: Install dependencies\n        run: composer install --no-interaction --prefer-dist\n\n      - name: Prepare the application\n        run: cp .env.example .env && php artisan key:generate\n\n      - name: Check layer boundaries\n        run: php artisan osdd:check --format=github\n\n      - name: Run tests\n        run: php artisan test\n",".github\u002Fworkflows\u002Ftests.yml","yaml",[203,602,603,615,619,642,646,654,661,671,678,691,695,706,714,731,736,749,760,765,777,787,792,804,814,819,831],{"__ignoreMap":224},[228,604,605,609,612],{"class":230,"line":231},[228,606,608],{"class":607},"swJcz","name",[228,610,611],{"class":437},":",[228,613,614],{"class":238}," tests\n",[228,616,617],{"class":230,"line":251},[228,618,482],{"emptyLinePlaceholder":481},[228,620,621,625,627,630,633,636,639],{"class":230,"line":478},[228,622,624],{"class":623},"sfNiH","on",[228,626,611],{"class":437},[228,628,629],{"class":437}," [",[228,631,632],{"class":238},"push",[228,634,635],{"class":437},",",[228,637,638],{"class":238}," pull_request",[228,640,641],{"class":437},"]\n",[228,643,644],{"class":230,"line":485},[228,645,482],{"emptyLinePlaceholder":481},[228,647,648,651],{"class":230,"line":500},[228,649,650],{"class":607},"jobs",[228,652,653],{"class":437},":\n",[228,655,656,659],{"class":230,"line":506},[228,657,658],{"class":607},"  tests",[228,660,653],{"class":437},[228,662,663,666,668],{"class":230,"line":517},[228,664,665],{"class":607},"    runs-on",[228,667,611],{"class":437},[228,669,670],{"class":238}," ubuntu-latest\n",[228,672,673,676],{"class":230,"line":522},[228,674,675],{"class":607},"    steps",[228,677,653],{"class":437},[228,679,680,683,686,688],{"class":230,"line":543},[228,681,682],{"class":437},"      -",[228,684,685],{"class":607}," uses",[228,687,611],{"class":437},[228,689,690],{"class":238}," actions\u002Fcheckout@v4\n",[228,692,693],{"class":230,"line":548},[228,694,482],{"emptyLinePlaceholder":481},[228,696,697,699,701,703],{"class":230,"line":573},[228,698,682],{"class":437},[228,700,685],{"class":607},[228,702,611],{"class":437},[228,704,705],{"class":238}," shivammathur\u002Fsetup-php@v2\n",[228,707,709,712],{"class":230,"line":708},12,[228,710,711],{"class":607},"        with",[228,713,653],{"class":437},[228,715,717,720,722,725,728],{"class":230,"line":716},13,[228,718,719],{"class":607},"          php-version",[228,721,611],{"class":437},[228,723,724],{"class":437}," '",[228,726,727],{"class":238},"8.3",[228,729,730],{"class":437},"'\n",[228,732,734],{"class":230,"line":733},14,[228,735,482],{"emptyLinePlaceholder":481},[228,737,739,741,744,746],{"class":230,"line":738},15,[228,740,682],{"class":437},[228,742,743],{"class":607}," name",[228,745,611],{"class":437},[228,747,748],{"class":238}," Install dependencies\n",[228,750,752,755,757],{"class":230,"line":751},16,[228,753,754],{"class":607},"        run",[228,756,611],{"class":437},[228,758,759],{"class":238}," composer install --no-interaction --prefer-dist\n",[228,761,763],{"class":230,"line":762},17,[228,764,482],{"emptyLinePlaceholder":481},[228,766,768,770,772,774],{"class":230,"line":767},18,[228,769,682],{"class":437},[228,771,743],{"class":607},[228,773,611],{"class":437},[228,775,776],{"class":238}," Prepare the application\n",[228,778,780,782,784],{"class":230,"line":779},19,[228,781,754],{"class":607},[228,783,611],{"class":437},[228,785,786],{"class":238}," cp .env.example .env && php artisan key:generate\n",[228,788,790],{"class":230,"line":789},20,[228,791,482],{"emptyLinePlaceholder":481},[228,793,795,797,799,801],{"class":230,"line":794},21,[228,796,682],{"class":437},[228,798,743],{"class":607},[228,800,611],{"class":437},[228,802,803],{"class":238}," Check layer boundaries\n",[228,805,807,809,811],{"class":230,"line":806},22,[228,808,754],{"class":607},[228,810,611],{"class":437},[228,812,813],{"class":238}," php artisan osdd:check --format=github\n",[228,815,817],{"class":230,"line":816},23,[228,818,482],{"emptyLinePlaceholder":481},[228,820,822,824,826,828],{"class":230,"line":821},24,[228,823,682],{"class":437},[228,825,743],{"class":607},[228,827,611],{"class":437},[228,829,830],{"class":238}," Run tests\n",[228,832,834,836,838],{"class":230,"line":833},25,[228,835,754],{"class":607},[228,837,611],{"class":437},[228,839,840],{"class":238}," php artisan test\n",[219,842,847],{"className":843,"code":845,"language":846},[844],"language-text","::error file=functional\u002Forders\u002Fsrc\u002FCheckout.php,line=7,title=Undeclared layer dependency::functional\u002Forders uses Functional\\Billing\\Models\\Invoice from functional\u002Fbilling but does not require functional\u002Fbilling.\n","text",[203,848,845],{"__ignoreMap":224},[199,850,851,852,855,856,859,860,327],{},"The annotation paths are relative to the Laravel root: when the application lives in a sub-folder of the repository, set ",[203,853,854],{},"working-directory"," on the step. Other CI systems can read ",[203,857,858],{},"--format=json","; see ",[358,861,863],{"href":862},"\u002Fcommands\u002Fosdd-check#json-format",[203,864,122],{},[199,866,867],{},"Unused requirements are warnings: they show as annotations but do not fail the step.",[869,870,871],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 .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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":224,"searchDepth":231,"depth":251,"links":873},[874,875,876,877],{"id":216,"depth":251,"text":217},{"id":330,"depth":251,"text":331},{"id":397,"depth":251,"text":398},{"id":579,"depth":251,"text":580},"Give each layer its own test suite, run one layer's tests, and fail pull requests that cross a boundary.","md",null,{},{"title":84,"description":878},{"loc":85},"o8OXjb9LZLNTZI7a9d64Vg7I0JKqBOjkvGyQBSIRR_s",[886,888],{"title":80,"path":81,"stem":82,"description":887,"children":-1},"Register the listeners of every layer without Event::listen(), like the ones of app\u002FListeners.",{"title":88,"path":89,"stem":90,"description":889,"children":-1},"Deploy an OSDD application so no request scans the layer folders, and know which cache to rebuild after a change.",1794437209293]