Strangle A YourBOX Legacy Route
When To Use
Section titled “When To Use”Use this runbook every time you port a legacy YourBOX route from the
2020 YourBOX/src/ tree to the Symfony application under app/src/.
The procedure is the same for every route: inventory, write the domain
code, write the controller, write the tests, verify, delete.
Preconditions
Section titled “Preconditions”- The verification baseline is in place (PHP 8.5, tests runnable, hadolint and Trivy gates green).
- The Symfony strangler skeleton lives at
app/with theLegacyBundlefallback active. - The bounded context targeted by the route is set up at
app/src/<Context>/per ADR-0006.
Procedure
Section titled “Procedure”-
Inventory the legacy route.
Terminal window grep -rn "<legacy_php_file_basename>" YourBOX/Expected result: every reference is listed. Note inputs (query string, POST fields, cookies, session keys), outputs (HTML, redirects, flash messages), database queries and any external side effect.
-
Confirm the bounded context.
The video upload route belongs to
Catalog, the comment posting toEngagement, the profile editing toIdentity, the admin toModeration. Place the new code underapp/src/<Context>/. -
Write the domain code.
- Add the entity or value object to
app/src/<Context>/Domain/. - Add the command (
UploadVideoCommand) and the handler (UploadVideoHandler) toapp/src/<Context>/Application/Command/. - Declare the outbound port in
app/src/<Context>/Application/Port/.
Verify:
Terminal window composer test:unit --working-dir=app --filter=<Command>HandlerTestExpected result: the unit tests for the handler pass.
- Add the entity or value object to
-
Write the adapter.
Add the Doctrine repository or the Flysystem adapter under
app/src/<Context>/Infrastructure/. Wire it inservices.yaml.Verify:
Terminal window composer test:integration --working-dir=app \--filter=<Adapter>TestExpected result: the integration tests pass against the local PostgreSQL and MinIO containers from
docker compose. -
Write the controller.
Add the controller under
app/src/<Context>/UserInterface/Controller/. The controller parses the request, dispatches the command through Symfony Messenger, renders the Twig template underapp/templates/<context>/<action>.html.twig. -
Write the functional test.
Add the
WebTestCaseunderapp/tests/Functional/<Context>/. Cover the happy path and the security path (CSRF rejected, auth required, role check).Verify:
Terminal window composer test:functional --working-dir=app \--filter=<Controller>TestExpected result: both tests pass.
-
Verify the strangler fallback no longer serves the route.
Terminal window curl --silent --output /dev/null --write-out "%{http_code}\n" \http://127.0.0.1:18080/<legacy_path>Expected result: HTTP 200 with the new template, not the legacy HTML.
Compare the new HTML against the legacy HTML snapshot:
Terminal window diff <(curl --silent http://127.0.0.1:18080/<legacy_path>) \tests/fixtures/legacy/<legacy_path>.htmlExpected result: only intentional differences (Tailwind classes, improved markup) appear.
-
Delete the legacy file.
Terminal window git rm YourBOX/src/<path_to_legacy_file>Search for remaining cross references:
Terminal window grep -rn "<legacy_php_file_basename>" YourBOX/Expected result: no result. Patch any remaining
require_oncechain if the search returns results. -
Update the migration status table on the Symfony Migration page.
-
Open the merge request.
The CI pipeline runs lint, test, scan, smoke, and the corresponding Plumber controls. Once green, request a review.
Validation
Section titled “Validation”composer test --working-dir=appexits 0 across unit, integration and functional suites.hadolint Dockerfile,trivy image,bash tests/smoke/dockerfile-smoke.shexit 0.- Plumber score stays at 100.
- The Grafana panel
YourBOX > Overview > Error Ratestays at baseline for the route’s class during the canary window.
If This Does Not Work
Section titled “If This Does Not Work”If the new controller does not claim the URL (the legacy fallback still fires), inspect the Symfony route list:
php app/bin/console debug:router | grep <legacy_path>Expected result: the new route appears with priority higher than the
legacy_fallback route’s -1000.
If a domain test cannot be written without booting Symfony, the domain
code is leaking the framework. Inspect the port interface in
Application/ and refactor so the test runs with stubs.
Escalate to the maintainer when:
- The legacy route depends on a global initialized by a chain of
require_oncecalls that does not fit the bounded context. - The HTML snapshot diff reveals an unintentional behavior change.
- The migration would require touching another bounded context’s domain code. Open a design discussion instead of forcing the cross context call.