-
-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
Author: Ciprian Ștefan Pleșca
This page describes the structural decomposition of OpenLongevity: the modules that exist, the responsibility each one owns exclusively, and the boundaries the codebase enforces between them. The guiding architectural principle, stated in the project's own README, is that different parts of the system "should be assessed individually" — a source adapter retrieving metadata is not the same claim as a database preserving revisions, which is not the same claim as a graph illustrating relationships.
flowchart TD
subgraph L1["Layer 1 — Ingress adapters (src/openlongevity/providers)"]
base["base.py: Provider contracts, Publication, Provenance"]
pubmed["pubmed.py: PubMedProvider"]
epmc["europe_pmc.py"]
openalex["openalex.py"]
crossref["crossref.py"]
ctgov["clinicaltrials.py"]
httpmod["http.py: shared transport helpers"]
end
subgraph L2["Layer 2 — Domain core (src/openlongevity)"]
models["models.py: EvidenceRecord, ResearchGap, Contradiction"]
evidence["evidence.py: EvidenceEngine"]
gaps["gaps.py: ResearchGapDetector"]
evgraph["graph.py: EvidenceGraph"]
constants["constants.py: DISCLAIMER"]
end
subgraph L3["Layer 3 — Persistence"]
db["db.py: Database, ORM rows"]
repo["repository.py: PublicationRepository"]
end
subgraph L4["Layer 4 — Interfaces"]
api["api.py: FastAPI app"]
cli["cli.py: argparse entry point"]
end
subgraph L5["Adjacent surfaces"]
biomarkers["biomarkers/: catalog"]
literature["literature/: adapter Protocol"]
analysis["analysis/: statistical utilities"]
rustcore["packages/rust/evidence-core"]
webapp["apps/web: TypeScript shell"]
end
L1 -->|normalized Publication| L3
L3 --> L4
L2 -->|synthetic fixtures| L4
L4 --> webapp
analysis -.independent, no cross-imports.-> L4
biomarkers -.reference catalog.-> L4
-
providers/base.pydefines the shared contract:SearchQuery(validated query, page, limit),Provenance(source provider, source identifier, checksum, normalization/parser versions),Publication(the normalized record shape),ClinicalTrial, and theLiteratureProviderProtocol. Every concrete provider must satisfy this protocol, which keeps ingestion behind a single narrow interface regardless of upstream API differences. -
providers/pubmed.pyimplementsPubMedProvideragainst NCBI E-utilities, with bounded response size (5 MB), rate limiting (asyncio.Lockplus a fixed 0.35 s delay to stay under 3 requests/second per instance), retry with exponential backoff on 429/5xx, and defused XML parsing to avoid unsafe entity expansion. -
models.pyis the single source of truth for domain enumerations (StudyType,RetractionStatus,FindingDirection,ReviewStatus) and the frozen dataclassesEvidenceRecord,ResearchGap, andContradiction. Validation lives in__post_init__, so an invalid record cannot be constructed. -
evidence.pyowns evidence grading and the navigation-score arithmetic (see Evidence-Grading-Engine). It has no knowledge of persistence or transport. -
gaps.pyconsumesEvidenceRecordsequences and theEvidenceEngineto detect five gap categories through explicit heuristics (see Research-Gap-Detection). -
graph.pyis an intentionally minimal in-memory adjacency structure (EvidenceGraph) used only for illustrative relationship demonstrations — it does not encode causal biological claims. -
db.pydeclares the SQLAlchemy ORM models (PublicationRow,PublicationRevisionRow) and an asyncDatabasewrapper. Schema evolution is exclusively through Alembic migrations, never through ad hoc DDL in application code. -
repository.pyimplementsPublicationRepository, the only component permitted to write topublicationsandpublication_revisions. It computes a content hash excluding the volatileretrieved_atfield, so identical re-ingestion does not fabricate a false revision. -
api.pyis the composition root: it wires providers, the repository, the evidence engine, and the gap detector into a FastAPI application, and is the only place where fixture-backed and persistence-backed routes coexist — explicitly labeled bymode. -
cli.pyis a thin, synchronous demonstration entry point; it does not touch the API, the database, or the provider layer, and always operates on a synthetic in-memory record.
sequenceDiagram
autonumber
participant Op as Operator
participant API as FastAPI (/api/v1/ingestion/pubmed)
participant Prov as PubMedProvider
participant NCBI as NCBI E-utilities
participant Repo as PublicationRepository
participant DB as PostgreSQL
Op->>API: POST body {query, limit} + X-Ingestion-Key
API->>API: secrets.compare_digest(key, configured key)
alt key missing or invalid
API-->>Op: 401 UNAUTHORIZED
else key valid
API->>Prov: search(SearchQuery)
Prov->>NCBI: esearch.fcgi (rate-limited, retried)
NCBI-->>Prov: id list (validated numeric)
Prov->>NCBI: efetch.fcgi (bounded to 5 MB)
NCBI-->>Prov: PubmedArticle XML (defused parse)
Prov-->>API: list[Publication] with checksum + provenance
loop each Publication
API->>Repo: save(publication)
Repo->>DB: INSERT ... ON CONFLICT DO NOTHING
Repo->>DB: SELECT ... FOR UPDATE
alt content changed
Repo->>DB: revision += 1, INSERT revision row
end
Repo-->>API: serialized record (revision, synthetic=False)
end
API-->>Op: PublicationPage (mode = "persisted")
end
-
Fixtures never reach the database. The
EvidenceRecordfixtures instantiated inapi.pyare process-local Python objects; nothing inevidence.pyorgaps.pyimportsrepository.py. -
The repository is the only writer. No other module constructs
PublicationRoworPublicationRevisionRowdirectly. -
Provenance is mandatory for persistence.
PublicationRepository.saveraisesValueErrorif aPublicationlacks aProvenancewith a checksum — a record with no traceable origin cannot be stored. - The ingestion key gates writes, not reads. Search and retrieval routes require no key; only the PubMed ingestion route, which triggers an outbound network call and a database write, is protected.
-
Analysis utilities are dependency-light and side-effect-free.
analysis/biological_age.py,analysis/survival.py, andanalysis/pathway.pyoperate purely on lists and sets passed by the caller — they do not import the API, the database, or the providers.
Continue to Domain-Model for the typed core, or Evidence-Grading-Engine for the scoring arithmetic.
© 2026 Ciprian Ștefan Pleșca. All rights reserved.
Licensed under the Apache License 2.0 — see LICENSE in the repository root.
OpenLongevity is research software. It does not provide medical advice, diagnosis, or clinical treatment recommendations.