Bharat · Edition for Examination
II · System architecture

A lakehouse for behavior.
A verdict at checkout.

Fig. 1 — Real-time decision pipeline on Databricks live
SOURCESBRONZE · RAWSILVER · CLEANEDGOLD · FEATURESDECISIONOrders DBUser AcctReturns APIBRONZEraw eventsSILVERnormalizedGOLDpod_pct, fail_rateDECISIONENGINE< 2 s SLAallow / restrict PODML CLASSIFIER
The four layers
  1. 01

    Bronze · raw events

    pod_risk.bronze_orders
    Append-only ingestion from transaction databases, user accounts and external return APIs. Schema-on-read, zero transformation. Auto Loader writes parquet into Delta.
  2. 02

    Silver · normalized

    pod_risk.silver_user_orders
    Deduplicated, joined to user master, normalized status enums (PLACED / REJECTED / RETURNED / DELIVERED). Indexed on user_id and ts via Liquid Clustering.
  3. 03

    Gold · per-user features

    pod_risk.gold_user_pod_behavior_30d
    Materialised feature store keyed by user_id. Trailing 30-day metrics: pod_percentage, pod_failure_rate, pod_rejected_30d, account_age_days, avg_basket_value_inr. Refreshed every 5 minutes by a Databricks workflow.
  4. 04

    Decision engine

    POST /api/decisions/evaluate
    Server function reads the user's gold row in a single SQL call, fires rule layer + ML classifier, returns allowed/blocked methods with a reason trail. Synchronous, p99 < 2s.
Data freshness

Five minute lag, end to end.

A scheduled Databricks Workflow recomputes the gold feature table every five minutes via Delta Live Tables. The decision engine reads only the gold row, so the SQL warehouse handles thousands of concurrent checkouts with a single indexed lookup.

Sample feature SQL
CREATE OR REPLACE TABLE pod_risk.gold_user_pod_behavior_30d
AS
SELECT
  user_id,
  ANY_VALUE(display_name)                                AS display_name,
  ANY_VALUE(city)                                        AS city,
  ANY_VALUE(months_between(current_date, signup_date))   AS tenure_months,
  COUNT(*)                                               AS total_orders_30d,
  SUM(CASE WHEN payment_type = 'POD' THEN 1 END)         AS pod_orders_30d,
  SUM(CASE WHEN payment_type='POD' AND status='REJECTED' THEN 1 END) AS pod_rejected_30d,
  SUM(CASE WHEN payment_type='POD' AND status='RETURNED' THEN 1 END) AS pod_returned_30d,
  SUM(CASE WHEN payment_type<>'POD' AND status='DELIVERED' THEN 1 END) AS prepaid_success_30d
FROM pod_risk.silver_user_orders
WHERE order_ts >= current_date - INTERVAL 30 DAYS
GROUP BY user_id;