Module 2 — Anatomy of a feature store
Module 1 left us with a promise: a single implementation of each feature, shared by training and serving. To keep that promise, a feature store is not one component but six that cooperate. This module names them, so the rest of the course can refer to them without ambiguity.
The six components
A feature store, whatever the vendor, is built around the same pieces.
The registry is the catalogue. It knows every feature the organization has declared, its owner, its data type, its freshness expectation, and the source it is computed from. It is what a new team member reads to discover what already exists before writing something similar.
The entities are the things features describe. In our fraud scoring, the natural entity is card_id — every feature is "something about this card". A merchant scoring model would have a merchant_id entity. A recommender would have both user_id and item_id. An entity is not a table; it is the join key by which features are looked up.
The feature views are logical groups of features that share the same entity and the same source. card_activity_1h might contain n_tx_last_1h, sum_amount_last_1h, n_distinct_countries_last_1h — three features that keep pace together because they are computed from the same rolling window on the same transaction stream.
The sources are where the raw data lives before any feature is computed: a Parquet directory on object storage for the fraud team, a Kafka topic of live authorizations, sometimes a Snowflake table for reference data. A source is described declaratively (path, format, timestamp column) so that both training and serving can point at it without embedding paths in application code.
The offline store holds the history of every feature at every timestamp. It is what a training pipeline queries to reconstruct, for each labelled event, the values features had at that time and not later. Parquet on S3 or GCS is the common choice; Snowflake, BigQuery and Redshift are equally usable.
The online store holds only the latest value of each feature per entity, indexed for millisecond lookups. Redis, DynamoDB and Cassandra are typical; a Postgres table with a good index works up to modest QPS. This is what the payment terminal actually queries.
Around them, a feature server exposes the online store over HTTP or gRPC. That is often optional in small setups — clients can hit the online store directly — but becomes valuable when features come from several stores or when authentication and audit are required.
How the pieces fit into MLOps
Placed next to the components of course 20, a feature store sits between the raw data and the model, on both sides.
At training time, the ML pipeline queries the offline store to build the training dataset. It does not touch raw files anymore; it hands the store an event log and receives features aligned to it. The trained model is registered as usual in the model registry.
At serving time, the model service queries the online store through the feature server (or directly). It never recomputes features from raw data. It never touches Parquet. That is the whole point: the model calls the store, the store answers with fresh values.
Between the two, one job matters: materialization. It reads the offline history, computes any aggregate that is not already stored, and pushes the latest values into the online store. Whether that is a nightly batch or a streaming job depends on the freshness the feature requires — module 6 will treat this in detail.
The registry, in a bit more detail
The registry deserves special attention because it is the piece that stops the organization from silently recreating the same feature under three names. It stores, per feature, a fully qualified name (card_activity_1h:n_tx_last_1h), a type, a description in plain English, an owner (a team, not a person), a freshness SLA (for instance "at most 5 minutes late"), and the tags that make discovery possible ("fraud", "card", "activity").
In practice the registry is a set of files versioned in git, plus a lightweight database that Feast or Tecton keeps in sync. That two-tier design is deliberate: the git files are the source of truth reviewed by code review, the database is the fast index consumed by the serving path.
A registry entry usually looks close to this in Feast:
from datetime import timedelta
from feast import Entity, FeatureView, Field, FileSource
from feast.types import Int64, Float64
card = Entity(name="card_id", join_keys=["card_id"])
source = FileSource(
name="tx_source",
path="s3://fraud/transactions/",
timestamp_field="ts",
)
card_activity_1h = FeatureView(
name="card_activity_1h",
entities=[card],
ttl=timedelta(days=30),
schema=[
Field(name="n_tx_last_1h", dtype=Int64),
Field(name="sum_amount_last_1h", dtype=Float64),
],
source=source,
owner="team-fraud@example.com",
tags={"domain": "fraud", "freshness": "5min"},
)
Notice how everything the training and serving paths need is declared: the entity's join key, the source's timestamp column, the schema, and metadata a governance tool can filter on.
Where mistakes concentrate
Two mistakes are worth naming now because every subsequent module will lean on the vocabulary.
Confusing entity and table. An entity is a join key, not a physical table. The same card can appear in transactions, disputes and rewards — three sources, one entity card_id. Modeling it as three separate entities forces artificial joins and, worse, permits divergent semantics for what a "card" is.
Storing features that are not features. A feature is a value per entity per timestamp. country_of_residence is a slowly-changing dimension: it belongs to a reference table read into a source, not to a feature view. Putting it into the feature store adds machinery without adding a store's benefit — freshness, point-in-time correctness, online lookup — that a dimension does not need.
A feature store is best understood as a contract: any consumer that asks for card_activity_1h:n_tx_last_1h at time gets the value that feature had at , computed by the one implementation declared in the registry. Everything in the anatomy — entities, views, sources, offline, online, server — exists to keep that contract, not to be a database.
Summary
- A feature store is six cooperating components: registry, entities, feature views, sources, offline store, online store, connected by materialization and exposed by an optional feature server.
- The registry is the catalogue where every feature is declared with owner, type and freshness expectations; it is what prevents silent duplication.
- Offline answers "what was this feature at time "; online answers "what is this feature now". Both come from the same declared source.
- The store is a contract between producers and consumers of features, not a database — the vocabulary of the next modules will refer to these pieces by name.
Next module: the offline and online stores in detail — why one is not enough, and how the two stay consistent.