Eligibility & Accumulators
The eligibility service answers, fast, the two questions adjudication asks of every claim line: is this member covered right now? and how much of their benefit have they already used? It owns no source of truth; it is a CQRS read-model built from enrollment events.
Field reference: full columns, types and nullability live in the catalog: glossary terms
MemberCoverage,CoverageAccumulator,DeductibleStatus,Coverage. This page is the narrative.
A read-model, not a source of truth
MemberCoverage is a denormalised projection. The enrollment service is the source of truth for policies and elements; eligibility consumes enrollment lifecycle events from Kafka (tracking position in projection_checkpoints) and writes flattened MemberCoverage rows so adjudication never has to join across enrollment tables.
This means it is eventually consistent: a just-issued policy becomes adjudicable only once its event has been projected. The trade is deliberate: adjudication reads are single-table and fast.
The three entities
MemberCoverage: one flattened row per covered member × element × term: the party/policy/element references, theelement_static_idcontinuity key, the in-forcecoverage_termssnapshot, and the coverage window. The window is a half-open interval[effective_from, effective_to):effective_tois null while active and is filled in on termination/cancellation. Status is a code-enforced closed set (ACTIVE | INACTIVE | TERMINATED), with no Go enum type.CoverageAccumulator: a running balance of how much of a benefit limit (a deductible, an out-of-pocket maximum, a category cap) a member has consumed within a term. Adjudication reads and updates accumulators to decide how much the plan pays on eachClaimLine. Continuity across element re-versioning is byelement_static_id.DeductibleStatus: a computed, non-persisted snapshot: the configured limit vs the amount consumed, exposing the remaining balance. It is derived on demand fromCoverageAccumulator+ coverage-term data.
Invariants
MemberCoverageis uniquely keyed by(party, element_static_id, term, effective_from).- A member's live entitlement is the set of
MemberCoveragerows with an open or in-range window on the claim's incident date. - Accumulators are scoped to a term; they reset at term boundaries and track continuously across element versions via
element_static_id.
Caveats
- Eventually consistent: eligibility owns no source of truth; enrollment does. A coverage gap usually means an unprojected or out-of-order event, not missing enrollment data.
- The deductible / out-of-pocket / copay vocabulary is US-style; on this UK-context platform these are placeholder benefit semantics.
term_idranges overolly:Termwhile the physical record isPolicyTerm(enrollment.policy_terms), an unreconciled naming question (see Policies).
