Skip to content

API versioning + strangler-fig migration

Two of the three HTTP backends are mid-migration. Each runs the legacy Flask + Cerberus surface and the FastAPI + Pydantic rewrite side-by-side in the cluster against the same MySQL schema. The shorthand for this pattern is strangler-fig — the new app grows around the old one, route-by-route, until nothing of the legacy app is reachable.

The API reference covers the final-state v2 / v4 surfaces. This page covers everything else: what's still on legacy, what's already moved, and how we plan to retire the residue.

Service version map

Service Legacy module Modern module Modern mount Status
cashless-backend apiserver/cm/ — Flask 2.0 apiserver/cm_v2/ — FastAPI /v2 Mid strangler-fig
personalaccount-api pa/api/endpoints/{v1,v2,v3}/ — Flask 2.3 pa_v4/ — FastAPI /v4 Mid strangler-fig
cashless-auth cashless-auth — FastAPI / Single version (v1)

Both legacy and modern apps deploy as separate Helm releases. ArgoCD syncs four backend Applications today:

ArgoCD app Values file Routes
cashless-backend deploy/argocd/values/cashless-backend.yaml Legacy Flask cm/ — internal only
cashless-backend-v2 deploy/argocd/values/cashless-backend-v2.yaml api.dev2.mycashless.com/v2
personalaccount-api deploy/argocd/values/personalaccount-api.yaml Legacy Flask pa/ — internal only
personalaccount-api-v4 deploy/argocd/values/personalaccount-api-v4.yaml pa-api.dev2.mycashless.com/v4

Strangler-fig shape

flowchart LR
    C([Client]) --> ING[GCE Ingress<br/>dev2.mycashless.com]
    ING -->|/v2/*| V2[cashless-backend-v2<br/>FastAPI · cm_v2]
    ING -->|/v4/*| V4[personalaccount-api-v4<br/>FastAPI · pa_v4]
    V2 -. internal HTTP .-> LEG1[cashless-backend<br/>Flask · cm/]
    V4 -. internal HTTP .-> LEG2[personalaccount-api<br/>Flask · pa/]
    V2 --> DB[(MySQL 8.0<br/>shared schema)]
    V4 --> DB
    LEG1 --> DB
    LEG2 --> DB

The ingress only routes external traffic to the FastAPI rewrites. The legacy Flask apps remain in the cluster so that not-yet-ported background jobs, internal admin tools, and the residual operator surfaces keep working. Both halves point at the same MySQL instance; migrations 020–032 made the schema a strict superset of what either app needs.

cashless-backendcm/cm_v2/

cm/ is Flask 2.0 on gunicorn + eventlet. It registers routes via a custom @wrappers.api_resource(...) decorator and validates payloads through cm/api/schemas.py — a single 4,267-LOC Cerberus blob (source).

cm_v2/ is FastAPI on uvicorn. Schemas live next to the routes in cm_v2/schemas/ as Pydantic models. The OpenAPI snapshot regression test pins 434 paths / 435 schemas as of Wave 9.

Side Module count Coverage
cm_v2/routers/ 76 routers (/v2) The full modern-app launch path: catalog, identity, devices, tickets, money movement, reports, events, payments.
cm/api/endpoints/ 97 files; 94 still on Flask Long-tail reference data, legacy admin surfaces not yet flipped in the admin UI, the legacy sqlreports runner.

Outstanding legacy categories (feature-level — full per-route list in .planning/HANDOFF-not-ported.md §1):

Area Examples (cm/api/endpoints/*.py)
Reference data + tooling androidapps, apiendpoints, banks, colors, countries, currencies, unit, unit_conversion, zipcodes
Legacy admin surfaces eventclients, eventconfig, marketplaceconfig, brand, areas, locations, bundle, categories, ticketcategories, ticketnumbers, producttype, product_cost, userproducts, modifiers, modifieritems, recipedetail
Reporting (legacy runner) sqlreports, reports, reportcustomdates, reportdevices
Internal / dev tests, version, auth (Flask shim)

Rough port budget: ~30–45 endpoint files of meaningful admin-facing routes plus the long tail of reference-data CRUDs. Pre-launch the rule is "ported when the admin UI feature is ported" — cm/ is what the admin UI talks to for anything not yet flipped.

personalaccount-apipa/pa_v4/

pa/ is Flask 2.3 on gunicorn. Endpoints are namespaced into three historical versions under pa/api/endpoints/:

Subpackage Mount in legacy Flask Files
pa.api.endpoints.v1 /v1 (auth, hosts, tickets, online transactions) 5
pa.api.endpoints.v2 /v2 (the bulk of cardholder + operator surface — 36 modules) 36
pa.api.endpoints.v3 /v3 (new payment SDKs — Adyen, Mercado Pago, PayPal, Stripe; devices; externaltransaction) 11

pa_v4/ is FastAPI on uvicorn — 48 routers under pa_v4/routers/ consolidate v1 + v2 + v3 onto a single surface mounted at /v4. Schemas live in pa_v4/schemas/ as Pydantic models. The OpenAPI snapshot regression test pins the full pa_v4 surface as of Wave 8.

Side Coverage
pa_v4/routers/ Cardholder core (me, users, userchips, accesses, connections), commerce + cart + reload, tickets, devices + chips, money movement, all four payment SDKs (Stripe / Adyen / MP / PayPal) + webhooks, mPOS-flavored operator surface.
pa/api/endpoints/ Legacy hosts + onlinetransactions (v1); long-tail v2 modules (androidapps, banks, client, company, cron, emails, fanki, openvoucher, orders, tablestages, verify, legacy refunds / transfers variants); v3 company, externaltransaction, plus legacy Stripe/PayPal/Adyen/MP entry-points still hit by older mobile builds.

Per-route status lives in .planning/HANDOFF-not-ported.md.

cashless-auth — single version

cashless-auth was greenfield FastAPI from day one. There is no strangler-fig active here — the service ships a single v1 surface covering password, phone OTP, Firebase token exchange, device, API key, and MFA flows. Source lives directly in the parent monorepo at services/cashless-auth.

Deprecation policy

Post-launch deprecation policy is TBD

Until launch, every backend lives on cashless-v2 and we forward-fix: when a legacy route is ported, the Flask implementation gets deleted in the same commit. No Deprecation headers, no Sunset dates, no parallel-deploy grace period.

Once cashless-v2 ships, this page will publish a written policy covering: minimum deprecation window per version, the Deprecation + Sunset response headers we emit, the changelog surface, and the cadence at which legacy mount paths get removed from the ingress.

No PRs pre-launch — even for strangler-fig moves

The parent monorepo and every submodule are on cashless-v2 until launch. Port a route by committing the new FastAPI router and the deletion of the corresponding cm/api/... or pa/api/... file in the same push — directly to cashless-v2. Don't open a PR, don't split the work across review rounds. See getting-started.md for the full branching policy.

Migration playbook + handoff docs

The three documents below carry the day-to-day truth about what has and hasn't moved:

  • .planning/v2-migration-playbook.md — cutover sequence, gates, rollback plan. Updated 2026-05-11 for the k8s-MySQL data-move (Phases 0–3 prefix the original §2.4 traffic flip).
  • .planning/migration-handoff.md — the live session-state snapshot. §1 is the source of truth for current submodule HEADs + test counts; §4 contains the agent task prompts.
  • .planning/HANDOFF-not-ported.md — exhaustive per-feature list of admin / cardholder surfaces still on Flask, with port estimates.

Wave history

Backend strangler-fig work is tracked alongside other modernization under a Wave numbering scheme. Recent waves:

Wave Closed Backend / migration outcome
Wave 4 2026-04-29 Stripe account.updated webhook lands in pa_v4; cm_v2 cross-tenant tests.
Wave 5 2026-04-30 Cross-customer + cross-tenant pytest suites; models alembic round-trip suite.
Wave 6 2026-05-08 KMP → Rust mobile SDK cutover (SDK, not backend). Backend side: modifiers leak fix.
Wave 7 2026-05-02 Shared ConsentService + cookie banner across all four frontends.
Wave 8 2026-05-05 ARS Stripe + per-country fee table; pa_v4 OpenAPI snapshot regression test.
Wave 9 2026-05-06 cm_v2 OpenAPI snapshot (1.96 MB / 434 paths / 435 schemas); iOS SDK swap continued.

Wave 6 is the SDK cutover, not a backend strangler-fig wave — listed for context because every wave-close commit bumps backend submodule pointers in the parent repo at the same time. See .planning/wave6-kmp-to-rust-migration.md for the SDK details.

Where to look next

  • API reference — Swagger / OpenAPI for the modern-state v2 / v4 / auth surfaces.
  • Architecture — full repo + service boundary map.
  • Services — every deployed host and the workload behind it.