docs(architecture): document profile and career ownership
CI and Deploy / test (push) Failing after 1m50s
CI and Deploy / deploy (push) Has been skipped

Add section 4a to docs/architecture/current.md: request flow, data ownership,
API responsibilities, and future extension points for the /profile vs /career
separation completed in Phase 2/2.2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cesnimda
2026-07-18 00:13:06 +02:00
parent 21c9b1ea63
commit cf8b2fa014
+73 -2
View File
@@ -120,9 +120,80 @@ Known consequence: a **dev-only 404 on deep links** follows directly from the Ne
**Styling:** MUI `sx` + custom `src/theme.ts` (439 lines), light/dark. Design tokens live inline in component `sx` props rather than in the theme (e.g. the same `boxShadow: "0px 8px 24px -12px rgba(15,23,42,0.12)"` is repeated across pages). There is no component primitives layer and no Storybook.
**Oversized components** (refactor targets, in order): `JobDetailsDialog.tsx` (1400), `ProfilePage.tsx` (1368), `JobTable.tsx` (786), `Correspondence.tsx` (732), `DashboardView.tsx` (666), `AdminSystemPage.tsx` (623), `AddJobModal.tsx` (618).
**Oversized components** (refactor targets, in order): `JobDetailsDialog.tsx` (1400), `CareerProfilePage.tsx` (1293), `JobTable.tsx` (786), `Correspondence.tsx` (732), `DashboardView.tsx` (666), `AdminSystemPage.tsx` (623), `AddJobModal.tsx` (618). (`ProfilePage.tsx` was 1368; Phase 2.2 split it — see §4a.)
`ProfilePage.tsx` serves **both** `/profile` and `/career`, forked by a `careerOnly` boolean — two nav destinations rendering one component.
---
## 4a. Profile / Career separation (Phase 2, 2026-07-17)
`/profile` and `/career` were one 1368-line component (`ProfilePage`) forked by a `careerOnly`
boolean. Phase 2 scoped their saves; Phase 2.2 split them into two dedicated components. This is
the reference model for how account identity and the master career profile relate.
### Components & routes
| Route | Component | Owns |
|---|---|---|
| `/profile` | `views/ProfilePage.tsx` (~490 lines) | Account identity + security + preferences |
| `/career` | `views/CareerWorkspacePage.tsx``views/CareerProfilePage.tsx` (~1293 lines) | The **master career profile** — the single editable source of truth |
`CareerWorkspacePage` is a thin shell (heading + source-of-truth notice) around `CareerProfilePage`.
The CV Builder is **not** built yet (Phase 4); `CareerProfilePage` is where it will live.
### Request flow
```
/profile → ProfilePage
load: GET /auth/me (account row only)
save: PUT /auth/profile { email, userName, firstName, lastName, displayName }
/career → CareerWorkspacePage → CareerProfilePage
load: GET /auth/me
GET /profile-cv/runs (extraction history)
GET /jobapplications?… (for per-job CV tailoring context)
save: PUT /auth/profile { profileCvText, profileCvStructureJson }
(ProfileCvController paths additionally dual-write CareerProfileService —
CareerProfiles / CareerProfileVersions — see §9 / §16)
```
### Data ownership (the invariant)
Both surfaces persist through the **same** endpoint, `PUT /auth/profile`, which does **partial
updates** (`AuthController.UpdateProfile`): a field is touched only if the request carries it —
`null`/omitted leaves it unchanged, `""` clears it, a value sets it. Email and UserName are never
cleared (login identifiers).
| Field(s) on `ApplicationUser` | Owner (only surface that writes them) |
|---|---|
| `Email`, `UserName`, `FirstName`, `LastName`, `DisplayName`, `AvatarImageDataUrl`, password, TOTP/2FA, linked OAuth accounts | **/profile** |
| `ProfileCvText`, `ProfileCvStructureJson` (the master career profile) | **/career** |
Because the endpoint is partial, `/profile` saving identity does **not** null the master profile,
and `/career` saving the profile does **not** null identity. This is enforced by tests
(`AuthAndSystemControllerTests`: identity-save-keeps-CV, career-save-keeps-identity, empty-clears,
null-leaves).
### API responsibilities
- `AuthController.UpdateProfile` (`PUT /auth/profile`) — partial update of the account row; the
single write path for both surfaces. Local accounts only.
- `AuthController` (`GET /auth/me`) — returns the whole account row; each surface reads the fields
it owns.
- `ProfileCvController` — CV ingest/parse/rewrite/export and the `CareerProfileService` dual-write.
Called from `/career`.
- `JobApplicationsController``/career` reads job list for tailoring context only.
### Future extension points
- **`CareerProfilePage` is the foundation for all future career outputs** (Phase 3/4): CV Builder,
tailored CVs, cover letters, portfolio, interview prep. They attach here, referencing the master
profile — never duplicating it (per `docs/MASTER_IMPLEMENTATION_GUIDE.md`).
- **Source-of-truth flip (F5):** today `ProfileCvStructureJson` is authoritative and
`CareerProfileService` mirrors it. A later phase makes `CareerProfiles`/`CareerProfileVersions`
authoritative; the `/career` save would then route through `CareerProfileService` rather than the
blob column. The partial-update endpoint and the ownership split above do not change.
- **Full decomposition (roadmap 2.2 residue):** `CareerProfilePage` is still large because it owns
the whole master-CV surface; the CV Builder work will extract sub-components from it.
---