docs(legibility): add per-domain README.md inside every domain package #400
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Part of Epic #394 — Documentation. This is DOC-6: a short
README.mdnext to the code, per domain, on both stacks. This is the documentation a developer hits when theycdinto the package they're about to work in.Per the Legibility Rubric, this addresses C7.1 (Major).
Required content (per domain README)
Each per-domain README is a single short file (≤80 lines). Template:
The frontend README mirrors this template with frontend-specific terms (routes, components, stores, hooks).
Scope: which packages
Backend (
backend/src/main/java/.../):document/README.mdperson/README.mdtag/README.mduser/README.mdgeschichte/README.mdnotification/README.mdocr/README.mdshared/README.md(covers the shared/ admission criteria + lists current members)Frontend (
frontend/src/lib/):document/README.md,person/README.md,tag/README.md,user/README.md,geschichte/README.md,notification/README.md,ocr/README.md,shared/README.mdconversation/README.md,activity/README.md(each explains the "derived domain" concept)Total: ~18 files (8 backend + 10 frontend).
Anti-patterns
docs/ARCHITECTURE.mdcontent. The per-domain README is shorter and more concrete.Acceptance criteria
README.md(8 files)README.md(10 files including derived domains)Dependency
Hard dependency on REFACTOR-1 (Epic 4) and REFACTOR-2 (Epic 4) — the per-domain READMEs are written into the new domain packages. This issue cannot start until the domain packages exist.
Optional: stub READMEs can be drafted earlier and committed during the refactor PRs themselves.
Definition of Done
All per-domain READMEs committed on
main. Closing comment lists the 18 created paths.🏗️ Markus Keller — Senior Application Architect
Observations
Hard dependency is real and correctly stated. The current backend is fully flat-layer packaged (
controller/,service/,repository/,model/). Writingdocument/README.mdbefore the domain packages exist means writing a README for a directory that doesn't exist yet — the file would be committed to the future location during REFACTOR-1/REFACTOR-2. This is fine as long as the stub strategy from the issue ("stub READMEs can be drafted earlier and committed during the refactor PRs themselves") is followed rigorously. Don't createREADME.mdfiles in the current flat paths — they'd be in the wrong place.Scope includes
geschichte/on the backend, which currently hasGeschichteController.java,GeschichteService.java,GeschichteRepository.java, andGeschichte.javain the flat packages. This domain exists but is not yet in its own package. The README should be authored assuming the final package layout, not the current flat layout.The
shared/README admission criteria is the most architecturally significant of the 18 files. Every team member currently importing cross-cutting utilities needs clarity on what qualifies forshared/vs being duplicated per domain. The issue template says "lists current members" — be specific: what concrete types/utilities currently belong there, and what the acceptance criteria are for adding future members.Cross-domain dependency section reveals coupling. Writing this section for
document/README.mdwill expose how many services reach into adjacent domains. This is a useful forcing function — document it honestly rather than aspirationally. IfDocumentServicecurrently callsPersonService,TagService, andFileService, all three should be listed.Frontend lib structure is not domain-organized.
frontend/src/lib/components/is currently a flat list of ~80 components. Writing frontend domain READMEs (e.g.,frontend/src/lib/document/README.md) requires REFACTOR-2 to move components into domain folders first. The issue correctly notes this dependency, but the flat frontend structure means the frontend READMEs are even more blocked than the backend ones.Recommendations
shared/README.md. It's the highest leverage: defines what counts as shared infrastructure and prevents premature generalization. This can be drafted now without waiting for REFACTOR-1.conversation/andactivity/are listed as "derived domains" butfrontend/src/routes/briefwechsel/handles conversations andfrontend/src/lib/stores/aktivitaeten/handles activity. Ensure the README path matches where files will actually live after REFACTOR-2.👨💻 Felix Brandt — Senior Fullstack Developer
Observations
~18 README files is a real authoring burden if done all at once. At 80 lines each, that's ~1440 lines of documentation to write, review, and keep accurate. The risk is that several files get written hastily to close the issue, then immediately go stale when REFACTOR-1/2 actually reorganizes the code.
The template section "Public surface (other domains may consume this)" is the most code-adjacent section. For it to be accurate, I'd need to audit the actual service method signatures. For example,
PersonServicecurrently hasgetById(),findOrCreate(),merge(), and search methods — but which of these are intended cross-domain API vs internal implementation? This isn't documented anywhere today.Frontend domain list includes
conversation/andactivity/, but these live underroutes/briefwechsel/andstores/aktivitaeten/right now. When writing the frontend README, use the post-REFACTOR-2 path (lib/conversation/README.md,lib/activity/README.md), not the current path. If the refactor hasn't happened yet, the README file literally can't be committed to the right place.OCR is split across two stacks in a non-obvious way. Backend has
OcrController,OcrService,OcrJobRepository,OcrTrainingService, etc. The frontend hasfrontend/src/lib/ocr/. But the actual OCR processing is in the Python microservice. Theocr/README.mdon the backend should be explicit: "this domain orchestrates OCR jobs, it does NOT perform OCR — that's the Python service." Similarly the frontend OCR README should call out the SSE streaming pattern.notification/README.mdneeds to capture the SSE emitter registry pattern.SseEmitterRegistry.javais a runtime-stateful component that manages SSE connections. It's unusual enough that a developer walking into this domain will be confused without context.Recommendations
shared/README.mdand one domain (e.g.,document/) as a gold-standard example first, then use that to calibrate the others. Don't try to write all 18 in one pass — quality degrades.grep -r "personService\." --include="*.java" | grep -v PersonService.javato find actual callers.CLAUDE.mdordocs/architecture/. The per-domain README is for the developer who justcd'd intobackend/src/main/java/.../document/— they want to know what this specific package owns, not the full system overview.🔒 Nora "NullX" Steiner — Application Security Engineer
Observations
This is a documentation issue, so there are no direct security vulnerabilities to flag. However, the content of the per-domain READMEs has security implications worth shaping before authoring starts.
The
security/package is conspicuously absent from the scope list. The issue lists 8 backend domains:document,person,tag,user,geschichte,notification,ocr,shared. Thesecurity/package (SecurityConfig,Permissionenum,@RequirePermission,PermissionAspect) exists as a separate package today. It should either get its own README or be covered inshared/README.md. Without documentation, developers joining the project won't know that@RequirePermissionis the sanctioned authorization mechanism — they may reach for@PreAuthorizewith magic strings instead.The "Public surface" sections for each domain should include permission requirements. For example,
DocumentServicemethods called from other services should note whether the caller needs to have already verified permissions, or whether the service method enforces its own access control. This is a security contract that currently lives only in code.user/README.mdshould explicitly state thatAppUseraccounts are never linked to historicalPersonentities. This domain boundary (documented in project memory) prevents identity confusion attacks — where someone might accidentally expose a historical person's records as if they belonged to a registered user. Hardcoding this in a README makes the separation undeniable to future developers.ocr/README.mdshould document the training token authentication pattern. The Python OCR service usesX-Training-Tokenheader auth for the/trainendpoint. The backend'sOcrTrainingServicecalls this endpoint. This security-relevant pattern should be mentioned in the backendocr/README so developers know that OCR training is authenticated.Recommendations
security/to the backend scope, or add a section toshared/README.mdthat covers it. Document: what@RequirePermissionis, where thePermissionenum lives, and that developers should never use@PreAuthorizewith string literals.user/README.md, include an explicit "NOT" statement: "This domain does NOT linkAppUseraccounts to historicalPersonentities. AnAppUseris a system user who can log in; aPersonis a historical family member. They are unrelated concepts."user/,ocr/) have their auth mechanisms documented in the README.🧪 Sara Holt — Senior QA Engineer
Observations
No testability angle on this issue — this is pure documentation. No CI verification is required for the README files themselves, and there is no runtime behavior to test. My role is minimal here.
The acceptance criteria are auditable but not automated. "Every backend Tier-1 domain package has a README.md (8 files)" is verifiable by
find . -name README.md -path "*/domain/*"in CI, but there's no CI gate proposed. Given this is docs-only, manual review on the PR is sufficient.The closing comment requirement ("lists the 18 created paths") is a good manual acceptance gate. That's exactly the right format for a closing checklist.
Risk: READMEs go stale immediately after the refactors. The "Frontend counterpart" cross-links between backend and frontend READMEs depend on the frontend having domain-organized folders, which doesn't exist yet (
frontend/src/lib/components/is flat). If backend READMEs are written first and link tofrontend/src/lib/document/README.mdbefore that path exists, the link is broken at merge time.Recommendations
find backend/src/main/java -name README.md | wc -lshould equal 8 andfind frontend/src/lib -maxdepth 2 -name README.md | wc -lshould equal 10. This is a 2-minute addition to the CI workflow and prevents partial-delivery merges.TODOplaceholder:Frontend counterpart: TODO — link after REFACTOR-2 completes. This way the link is honest rather than broken.🎨 Leonie Voss — UX Designer & Accessibility Strategist
Observations
This is a developer-facing documentation issue with no user interface impact. The per-domain READMEs are consumed by developers, not end users. From a UX and accessibility perspective, I have nothing to flag on the feature itself.
However, there's one oblique concern worth naming:
The frontend domain READMEs, when they exist, will document accessibility-relevant patterns. The
document/README.mdandgeschichten/README.mdfrontend READMEs should note any domain-specific accessibility commitments — for example, thatGeschichtenCard.svelterequiresaria-labelfor the story action buttons, or that thePersonChipRow.sveltecomponent uses keyboard-navigable chips. These are currently undocumented. If we're going to write READMEs, this is the right place to capture "here's what accessibility means for components in this domain."The audience split matters for the frontend READMEs. The project has two distinct audiences: transcribers (60+ on laptop/tablet) and readers (younger, on phone). The
geschichten/frontend README should explicitly note which audience it primarily serves (readers on mobile) since this affects component decisions like touch target sizing and layout density.Recommendations
geschichten/: "All interactive cards must have 44px touch targets. Story titles must not rely on color alone for status."⚙️ Tobias Wendt — DevOps & Platform Engineer
Observations
This is a documentation issue with no infrastructure, CI, or deployment implications. No Docker changes, no new services, no secrets involved.
The only infrastructure-adjacent point I'd raise:
The
ocr/domain READMEs (both backend and frontend) should document the service boundary clearly for ops purposes. When I'm debugging a production issue and OCR jobs are failing, I need to know quickly: is this a backend orchestration problem (OcrService,OcrJobRepository), a frontend SSE streaming problem, or a Python microservice problem (ocr-service/)? The backendocr/README.mdshould include a one-liner on the Python service: "OCR processing is delegated toocr-service/(Python, port 8000, separate Docker container). This domain handles job lifecycle only."No CI gate is proposed for verifying all 18 files exist. Sara flagged this too. From a pipeline perspective, a
find-based count check in the CI workflow is trivial. I'd add it to the same job that runsmvn verifyornpm run check— it costs nothing and prevents half-merges. Example:Recommendations
ocr/README cross-reference toocr-service/(Python) should be explicit so on-call ops don't have to reverse-engineer which layer failed when debugging.📋 Elicit — Requirements Engineer
Observations
The issue is well-structured with a clear template, scope list, anti-patterns section, and acceptance criteria. For a documentation task, this is unusually thorough. A few gaps worth naming:
The hard dependency is stated but the unblocking path is underspecified. "This issue cannot start until the domain packages exist" — but the issue is already open and in the milestone. Without knowing when REFACTOR-1 and REFACTOR-2 are scheduled, there's no clear signal for when this issue can actually be picked up. If those refactors don't have scheduled PRs, this issue may sit open indefinitely.
The stub README strategy ("stub READMEs can be drafted earlier and committed during the refactor PRs themselves") is optional but potentially the most pragmatic path. The issue doesn't commit to this path — it says "can be." Recommend making this the default plan: stubs committed with each refactor PR, completed during this issue's PR.
"Tier-1 domain" is used in the acceptance criteria but not defined in the issue. The scope section lists 8 backend packages, but doesn't explain why
audit/,config/,dashboard/,relationship/, andsecurity/are excluded. Developers reading this issue in 6 months won't know whetheraudit/was intentionally excluded or just forgotten. The non-obvious omissions should be stated explicitly as "NOT in scope."The acceptance criteria item "Each backend README links to its frontend counterpart" is not verifiable until after REFACTOR-2. This creates a situation where the PR could pass all other ACs but fail this one if the frontend refactor isn't done yet. Either split this AC into a separate issue, or note that cross-links are deferred until after both refactors.
No owner or milestone date. The issue is assigned to "Codebase Legibility" milestone but has no assignee and no target date. For a solo developer, this is fine operationally, but the hard dependency means this issue needs to be re-triaged once REFACTOR-1 has a timeline.
Recommendations
audit/,config/,dashboard/,relationship/,security/,exception/,dto/— with one sentence on why each is excluded (e.g., "config/ and exception/ are infrastructure, not domains; security/ is cross-cutting and covered in shared/README").✅ Decisions Resolved
This issue does not have a consolidated Decision Queue comment, but Elicit, Markus, and Sara raised implicit decisions in their reviews. Resolving them here for completeness.
1. Out-of-scope packages → explicitly excluded with rationale
Per epic-level D2 (resolved on #394), the canonical DOC-6 enumeration is 18 files:
Backend (9):
document/,person/,tag/,user/,geschichte/,notification/,ocr/,audit/,dashboard/Frontend (8):
document/,person/,tag/,user/,geschichte/,notification/,ocr/,shared/OCR service (1):
ocr-service/README.mdExplicitly excluded (per Elicit's flag — this MUST appear in the README that introduces DOC-6, so reviewers know the omissions are deliberate, not forgotten):
config/— Spring infrastructure, no domain identity. Conventions covered in CONTRIBUTING.md (#398).exception/—DomainException+ErrorCode+GlobalExceptionHandler. Cross-cutting; documented in CONTRIBUTING.md "Error handling" section.security/—SecurityConfig,Permissionenum,@RequirePermission,PermissionAspect. Per Nora's flag, this content must appear indocs/ARCHITECTURE.mdsecurity model section (#396) AND in CONTRIBUTING.md "Security checklist" (#398). It does not need its own per-domain README.filestorage/—FileService(S3/MinIO wrapper). Cross-cutting infrastructure.importing/—MassImportService. Documented in CONTRIBUTING.md as a walkthrough or inimport/CLAUDE.mdmigration target (DOC-7).The original issue body listed only
document/,person/,tag/,user/,geschichte/,notification/,ocr/,shared/for the backend (8) — the revised list is 9 backend files, addingaudit/anddashboard/per epic D2 and Markus's audit observations. Update the issue's scope section to match.2. Stub strategy → commit stubs during refactor PRs; complete in DOC-6 PR
Per Elicit and Markus: change "Optional: stub READMEs can be drafted earlier" to "Plan: stub READMEs are committed alongside each refactor PR (Epic 4 / REFACTOR-1, REFACTOR-2). DOC-6 PR completes them." Each stub carries a header note: "Note: this domain is being migrated as part of Epic 4 — README will be expanded once the refactor lands."
This unblocks the issue's hard dependency on REFACTOR-1/2: the work happens incrementally, not in one big-bang PR.
3. Cross-link AC → defer "links to counterpart" until both stacks refactored
Per Sara: "Each backend README links to its frontend counterpart" cannot land until REFACTOR-2 finishes. Resolution:
TODO — link after REFACTOR-2 completes.📌 Additional persona feedback to fold into implementation
shared/README.md— highest leverage; defines what counts as shared infrastructure; can be drafted now without waiting for refactor. Be specific: list current concrete types/utilities that belong inshared/, plus the admission criteria from #387 (no entity, no CRUD, ≥2 consumers OR framework infra).briefwechsel/(= conversation),aktivitaeten/(= activity),stammbaum/(= person stammbaum view). Ensure README path matches where files actually live after REFACTOR-2.shared/and one domain (e.g.,document/) as a gold standard first, then calibrate the others. Don't try to write all 18 in one pass — quality degrades.grep -r "personService\." --include="*.java" | grep -v PersonService.javato find actual cross-domain callers.user/README.md— explicit "does NOT" statement: "This domain does NOT linkAppUseraccounts to historicalPersonentities. AnAppUseris a system user who can log in; aPersonis a historical family member. They are unrelated concepts." Per memoryproject_person_appuser_separation, this is a load-bearing project rule.ocr/README.md(backend) — note the X-Training-Token authentication pattern and that OCR processing is delegated toocr-service/(Python).ocr/README.md(backend) — explicit boundary statement: "This domain orchestrates OCR jobs. It does NOT perform OCR — that's the Python service inocr-service/."notification/README.md— capture theSseEmitterRegistrypattern (runtime-stateful component managing SSE connections) so a developer walking in isn't confused.find backend/src/main/java -name "README.md" | wc -l≥ 9 ANDfind frontend/src/lib -maxdepth 2 -name "README.md" | wc -l≥ 8 (after refactor). Trivial to add; prevents partial-merge.docs/STYLEGUIDE.mdfor the full WCAG checklist.geschichten/, note primary audience is mobile readers (per memoryproject_user_split_transcribe_vs_read); for transcribers, primary audience is laptop/tablet.Status: Mostly ready, but hard-blocked by Epic 4 (refactor) for the per-domain backend READMEs. The non-blocked file is
shared/README.md— that can be authored now. The 17 others need their target package paths to exist.DOC-6 implemented — PR #444
18 per-domain README.md files created:
Backend (9):
backend/.../document/README.mdbackend/.../person/README.mdbackend/.../tag/README.mdbackend/.../user/README.mdbackend/.../geschichte/README.mdbackend/.../notification/README.mdbackend/.../ocr/README.mdbackend/.../audit/README.mdbackend/.../dashboard/README.mdFrontend (8):
frontend/src/lib/document/README.mdfrontend/src/lib/person/README.mdfrontend/src/lib/tag/README.mdfrontend/src/lib/user/README.mdfrontend/src/lib/geschichte/README.mdfrontend/src/lib/notification/README.mdfrontend/src/lib/ocr/README.mdfrontend/src/lib/shared/README.mdOCR service (1):
ocr-service/README.mdPR: http://heim-nas:3005/marcel/familienarchiv/pulls/444