docs/adr/ADR-0007-unified-feature-transformations.md
Accepted
In Feast, the OnDemandFeatureView name did not clearly convey that transformations execute at read time. The term "On Demand" was ambiguous about when the transformation occurs. Additionally, there were multiple feature view types (FeatureView, BatchFeatureView, StreamFeatureView, OnDemandFeatureView) with:
FeatureStore.apply(), get_online_features, get_historical_features).| Type | When Transformation Occurs | Where | Materialization |
|---|---|---|---|
| FeatureView | Undefined | Outside Feast | Feature Server or Batch |
| BatchFeatureView | Batch process | Offline Store (external) | Feature Server or Batch |
| StreamFeatureView | Streaming process | Stream Processor (external) | Stream Processor |
| OnDemandFeatureView | On Read | Feature Server | Feature Server |
| OnDemandFeatureView (writes) | On Write | Feature Server | Feature Server |
Unify Batch, Streaming, and On-Demand feature views into a single FeatureView class with a @transform decorator that makes execution timing explicit.
from enum import Enum
class FeastTransformation(Enum):
NONE = 0 # No transformations (default)
ON_READ = 1 # Current On Demand Feature View behavior
ON_WRITE = 2 # Transformations at write time
BATCH = 3 # Batch processing transformations
STREAMING = 4 # Stream transformations
@transform(
type=FeastTransformation.ON_WRITE,
schema=[...],
entity=[...],
sources=[...],
mode="python", # pandas, substrate, etc.
engine="feature_server", # Spark, Snowflake, etc.
orchestrator=None, # Airflow, KFP, etc.
)
def my_feature_view(inputs):
outputs = {
"my_feature": [v * 1.0 for v in inputs["input_feature_1"]],
}
return outputs
The codebase currently uses a transformation() decorator and TransformationMode enum (with modes like PANDAS, PYTHON, SPARK, RAY, SQL, SUBSTRAIT) in sdk/python/feast/transformation/base.py. The legacy OnDemandFeatureView, StreamFeatureView, and BatchFeatureView classes still exist during the migration period.
sdk/python/feast/transformation/base.py