Architecture¶
IRIS is structured around Domain-Driven Design (DDD) with a Hexagonal Architecture (Ports & Adapters).
Core principles¶
Pure domain — domain classes have zero infrastructure dependencies. They hold rules, invariants, and value objects, not I/O.
Immutable objects — every domain class is @define(frozen=True) via attrs. State is never mutated; attrs.evolve() is used to produce new instances.
Ports & Adapters — the domain declares what it needs (ports); adapters implement how it is fulfilled (Tabix files, VCFs, databases).
Layer map¶
┌─────────────────────────────────────────────┐
│ Domain │
│ generic/ genomics/ lims/ hr/ … │
│ (pure Python, no I/O) │
└──────────────────┬──────────────────────────┘
│ ports (ABCs)
┌──────────────────▼──────────────────────────┐
│ Adapters (driven) │
│ readers/ annotators/ stores/ │
│ exporters/ pipeline.py │
│ (pysam, zarr, numpy, openpyxl, …) │
└─────────────────────────────────────────────┘
Domains¶
| Domain | Description |
|---|---|
generic |
Shared bases: Metadata, AliasSet, NamedEntity, repository ABCs |
genomics |
Variants, genes, annotations, filters — the primary domain |
lims |
Patients, samples, assays, reports |
human_resources |
Staff, roles, contracts |
operations |
Operational workflows |
marketing |
Referrals, campaigns |
sysadmin |
System configuration and users |
Applications¶
Repository hierarchy¶
Repository[T] get(id) → Optional[T]
├── ReadRepository[T] + find_all() → Sequence[T]
├── WriteRepository[T] + save(T) → T, delete(id)
└── CrudRepository[T] ReadRepository + WriteRepository
Genomics repositories are read-only (Repository[T]). LIMS, HR, and marketing repositories are CrudRepository[T].
Variant extraction pipeline¶
VariantExtractionPipeline orchestrates a two-phase workflow:
- Extraction —
VariantAnnotationStore.fetch(region)is called in parallel across all regions using aThreadPoolExecutor. Zarr stores release the GIL for array reads, so multi-threading yields near-linear speedups. - Annotation — collected
VariantAnnotationobjects pass through eachVariantAnnotatorsequentially.
regions / variants
│
▼
VariantExtractionPipeline
├── VariantAnnotationStore.fetch() ← parallel (N workers)
│ └── ZarrVariantStore ← thread-safe Zarr reads
└── VariantAnnotator.annotate() ← sequential batch
├── ClinvarAnnotator
└── WesLofteeAnnotator
VariantStore[T] is symmetric to Annotator[T]: annotators enrich existing
VariantAnnotation objects; stores produce them from raw data sources.
Genomic coordinates¶
GenomicPosition uses 1-based inclusive coordinates throughout the domain.
Conversions happen at adapter boundaries:
- BED (0-based half-open) → converted in
BedRegionReader - pysam
.fetch()(0-based half-open) →start - 1at call site - vcf2zarr
variant_position→ already 1-based (VCF native), no conversion needed inZarrVariantStore