Architecture¶
denselinkage is built ports-and-adapters (hexagonal): a small, dependency- free contract at the centre, with every replaceable part behind a typed port. The architecture is the project’s primary design goal — this page is the public overview; the full design record (ADRs and decision logs) lives in the repository, linked at the bottom.
The contract is the centre¶
denselinkage.core holds only what the contract needs:
the ports (typing.Protocols), the domain models, the results
ports reference, and the error taxonomy. It depends on nothing heavy. Every
other module either implements a port here or orchestrates ports defined here.
First-party adapters subclass their port explicitly, so mypy verifies each implementation is complete; third-party code may conform structurally without importing anything.
The dependency rule¶
The core stays light (NumPy + pandas). Heavy backends — FAISS, sentence-transformers, LangChain — are optional extras that adapters import lazily, inside the methods that use them. Consequences:
import denselinkagenever pulls a heavy backend.A missing extra surfaces only when you construct that specific adapter.
The documentation you are reading builds with no extras installed at all.
This cut is enforced by a test, not just convention.
Spec → artifact¶
Stateful work is split into a stateless spec and an immutable artifact:
A
Blockerspec builds aBlockingIndexartifact.A
VectorIndexspec builds aSearchableIndexartifact.
build() is a factory: it returns fresh state and mutates neither self nor its
arguments. So one spec safely builds many artifacts, and an artifact is safe to
reuse across queries. The same split appears at the top level —
DenseLinker (immutable config) versus
LinkageIndex (prepared state). (ADR-0001.)
Evaluation lives outside the core¶
The metric report types live in denselinkage.metrics, not
in core, because nothing in the contract depends on them — the dependency rule
applied to the evaluation layer. (ADR-0002.)
A curated, two-tier surface¶
The public API is deliberately shaped (see the API reference):
the prelude for everyday use, capability submodules for the adapters, and
denselinkage.core for the contract you implement against. The surface is
frozen and evolves by extend, never modify — new optional fields, sibling
types, and new ports are additive; existing signatures and field types are
stable. (ADR-0003.)
The full design record¶
Architecture Decision Records and the development decision log are kept in the repository, version-controlled alongside the code rather than published here:
Architecture Decision Records — spec→artifact (0001), evaluation-out-of-core (0002), pre-freeze ratification (0003).
Development docs — the roadmap, the freeze gate, the decision log, and the Resolvi reference-architecture conformance analysis.