|
| 1 | +# ADR 0001 — Collapse to a Two-Service Architecture (Python single backend) |
| 2 | + |
| 3 | +- **Status:** Accepted (in progress) |
| 4 | +- **Date:** 2026-06-07 |
| 5 | +- **Supersedes:** the three-tier polyglot architecture described in `system-overview.md` |
| 6 | + |
| 7 | +## Context |
| 8 | + |
| 9 | +The application runs a three-tier polyglot stack: |
| 10 | + |
| 11 | +``` |
| 12 | +React frontend (31k LOC) |
| 13 | + │ only ever talks to :5001 |
| 14 | + ▼ |
| 15 | +.NET gateway (21k LOC + 23k test) ──HTTP proxy──► Python engine (17.5k LOC + 16k test) |
| 16 | + auth · MongoDB · jobs · SignalR · ALL real compute (astropy): |
| 17 | + storage · + proxies Composite/Mosaic/ composite, mosaic, mast, |
| 18 | + Mast/Analysis/Semantic to Python analysis, discovery, semantic |
| 19 | + │ │ |
| 20 | + ▼ ▼ |
| 21 | + MongoDB (2 collections) MAST + shared storage (SeaweedFS = 4 containers) |
| 22 | +``` |
| 23 | + |
| 24 | +A full-stack review surfaced that the `.NET` `CompositeService`, `MosaicService`, |
| 25 | +`MastService`, `AnalysisService`, and `SemanticSearchService` are **HTTP proxies** |
| 26 | +(`_httpClient` → Python). The Python engine already owns every one of those domains |
| 27 | +end-to-end. The `.NET` tier's only *non-duplicated* responsibilities are: |
| 28 | + |
| 29 | +- **Auth** (JWT issue/validate, register/login/refresh) |
| 30 | +- **MongoDB persistence** — only two collections: `jwst_data` and `users` |
| 31 | +- **Job tracking + SignalR push** (`/hubs/job-progress`) and its background workers |
| 32 | + (composite/mosaic/thumbnail/embedding/scan/reaper) |
| 33 | +- **Storage abstraction** — already duplicated by Python's `app/storage/provider.py` |
| 34 | + |
| 35 | +Astropy and the scientific stack must stay in Python. The middle tier therefore adds |
| 36 | +a second language, a second service, and a `snake_case ↔ camelCase` boundary while |
| 37 | +mostly forwarding requests. |
| 38 | + |
| 39 | +## Decision |
| 40 | + |
| 41 | +Make the **Python FastAPI service the single backend** and **delete the .NET gateway**. |
| 42 | +The Python backend absorbs auth, MongoDB persistence, job tracking, and real-time |
| 43 | +progress (WebSocket replacing SignalR). The React frontend talks to one backend over |
| 44 | +HTTP + WebSocket. |
| 45 | + |
| 46 | +### Target architecture |
| 47 | + |
| 48 | +``` |
| 49 | +React frontend ──HTTP + WebSocket──► Python FastAPI (single backend) |
| 50 | + compute + auth + persistence + jobs + WS |
| 51 | + │ |
| 52 | + ▼ |
| 53 | + MongoDB · storage (volume/real S3) · MAST |
| 54 | +``` |
| 55 | + |
| 56 | +## Consequences |
| 57 | + |
| 58 | +**Positive** |
| 59 | + |
| 60 | +- One backend instead of two; one language deleted (~44k lines of C# incl. tests). |
| 61 | +- No more cross-language DTO/casing boundary. |
| 62 | +- Far fewer containers and compose files; simpler local and prod topology. |
| 63 | +- Each ported responsibility carries its security fix from the review |
| 64 | + (JWT-secret-from-env, password complexity, no seed passwords, locked job state, |
| 65 | + `asyncio.to_thread` for blocking `fits.open`, no `str(e)` leakage). |
| 66 | + |
| 67 | +**Negative / risks** |
| 68 | + |
| 69 | +- Auth, persistence, and job orchestration must be reimplemented in Python. |
| 70 | +- SignalR is replaced by a WebSocket protocol; the frontend client is rewritten once. |
| 71 | +- A single frontend cutover (Phase 5) is the riskiest step; it is reversible by |
| 72 | + reverting the API base URL while both backends still run. |
| 73 | + |
| 74 | +## Migration roadmap (strangler-fig) |
| 75 | + |
| 76 | +The system stays shippable after every phase. Phases 1–4 add Python capability behind |
| 77 | +the scenes while .NET still serves the frontend; Phase 5 is the one switchover; Phase 6 |
| 78 | +deletes the gateway. |
| 79 | + |
| 80 | +| Phase | Scope | |
| 81 | +|-------|-------| |
| 82 | +| 0 | This ADR + Python backend package scaffolding (`auth/`, `db/`, `library/`, `jobs/`) + deps | |
| 83 | +| 1 | MongoDB (`motor`) + Auth (`pyjwt`, `passlib`) in Python | |
| 84 | +| 2 | Library / persistence endpoints (`jwst_data` CRUD, upload, scan) | |
| 85 | +| 3 | Jobs + WebSocket progress + background workers | |
| 86 | +| 4 | MAST import + discovery wired to persistence/jobs | |
| 87 | +| 5 | Frontend cutover to Python + WebSocket | |
| 88 | +| 6 | Delete the `.NET` tier (`backend/`, CI jobs, compose services) | |
| 89 | +| 7 | Infrastructure simplification (SeaweedFS → volume/S3, collapse compose files) | |
| 90 | +| 8 | In-tier cleanup (god-files, duplication) | |
| 91 | + |
| 92 | +## References |
| 93 | + |
| 94 | +- `CODEBASE_REVIEW.md` — full-stack review and issue inventory |
| 95 | +- `system-overview.md` — current (pre-migration) architecture |
0 commit comments