How Neobanks Build Real-Time Data Infrastructure with RisingWave

How Neobanks Build Real-Time Data Infrastructure with RisingWave

Neobanks build real-time data infrastructure with RisingWave by replacing scheduled batch jobs and polling-based caches with streaming materialized views that update within milliseconds of each transaction, account change, or user event—enabling instant push notifications, live balance updates, and real-time spending insights without the complexity of hand-coded stream processing frameworks.

Why Neobank Data Infrastructure Is Different

Neobanks compete primarily on user experience. While traditional banks refresh account dashboards nightly, neobanks show transactions in real time. While incumbents send fraud alerts by SMS the next morning, neobanks block suspicious transactions instantly. While legacy institutions run credit models monthly, neobanks prequalify users live.

This UX expectation translates directly into a data infrastructure requirement: every customer-facing feature must be backed by data that is seconds-fresh, not hours-stale. Traditional data warehouse architectures cannot meet this requirement. Neither can custom stream processing frameworks without significant engineering investment.

RisingWave gives neobank engineering teams a SQL-native streaming layer: define what you want to compute as a materialized view, and the system keeps it current automatically. No Flink jobs. No custom Kafka Streams topologies. Just SQL.

Core Neobank Event Streams

-- Account activity events (transactions, fee charges, interest accruals)
CREATE SOURCE account_events (
    event_id            VARCHAR,
    account_id          VARCHAR,
    user_id             VARCHAR,
    event_type          VARCHAR,    -- 'DEBIT', 'CREDIT', 'FEE', 'INTEREST', 'REFUND'
    amount              DECIMAL(18,2),
    running_balance     DECIMAL(18,2),
    currency            VARCHAR(3),
    merchant_name       VARCHAR,
    merchant_category   VARCHAR,
    payment_method      VARCHAR,    -- 'CARD', 'ACH', 'INSTANT', 'P2P'
    event_time          TIMESTAMPTZ
) WITH (
    connector = 'kafka',
    topic = 'account-events',
    properties.bootstrap.server = 'kafka:9092',
    scan.startup.mode = 'latest'
) FORMAT PLAIN ENCODE JSON;

-- User app behavior events
CREATE SOURCE user_app_events (
    session_id          VARCHAR,
    user_id             VARCHAR,
    event_name          VARCHAR,    -- 'APP_OPEN', 'FEATURE_VIEW', 'NOTIFICATION_TAP'
    feature_name        VARCHAR,
    device_type         VARCHAR,
    app_version         VARCHAR,
    event_time          TIMESTAMPTZ
) WITH (
    connector = 'kafka',
    topic = 'user-app-events',
    properties.bootstrap.server = 'kafka:9092',
    scan.startup.mode = 'latest'
) FORMAT PLAIN ENCODE JSON;

Real-Time Spending Insights

The signature feature of neobanks is instant categorized spending insights. With RisingWave:

-- Monthly spending by category (resets each calendar month)
CREATE MATERIALIZED VIEW monthly_spending_by_category AS
SELECT
    user_id,
    DATE_TRUNC('month', event_time)     AS month,
    merchant_category,
    SUM(amount) FILTER (WHERE event_type = 'DEBIT')     AS spent,
    SUM(amount) FILTER (WHERE event_type = 'REFUND')    AS refunded,
    SUM(amount) FILTER (WHERE event_type = 'DEBIT')
        - SUM(amount) FILTER (WHERE event_type = 'REFUND') AS net_spent,
    COUNT(*) FILTER (WHERE event_type = 'DEBIT')        AS transaction_count,
    MAX(event_time)                                     AS last_transaction
FROM account_events
WHERE event_type IN ('DEBIT', 'REFUND')
GROUP BY user_id, DATE_TRUNC('month', event_time), merchant_category;

-- Weekly spending trend for dashboard charts
CREATE MATERIALIZED VIEW weekly_spending_trend AS
SELECT
    window_start,
    window_end,
    user_id,
    SUM(amount) FILTER (WHERE event_type = 'DEBIT')     AS total_spent,
    COUNT(*) FILTER (WHERE event_type = 'DEBIT')        AS transaction_count,
    AVG(amount) FILTER (WHERE event_type = 'DEBIT')     AS avg_transaction,
    COUNT(DISTINCT merchant_category)                   AS category_diversity,
    SUM(amount) FILTER (WHERE payment_method = 'CARD')  AS card_spend,
    SUM(amount) FILTER (WHERE payment_method = 'P2P')   AS p2p_spend
FROM TUMBLE(account_events, event_time, INTERVAL '7 DAYS')
GROUP BY window_start, window_end, user_id;

Real-Time Fraud Detection for Card Transactions

-- Velocity checks: transactions per user in short windows
CREATE MATERIALIZED VIEW transaction_velocity AS
SELECT
    window_start,
    window_end,
    user_id,
    COUNT(*)                                        AS tx_count,
    SUM(amount)                                     AS total_amount,
    COUNT(DISTINCT merchant_name)                   AS distinct_merchants,
    MAX(amount)                                     AS max_single_tx,
    COUNT(*) FILTER (WHERE payment_method = 'CARD') AS card_tx_count
FROM TUMBLE(account_events, event_time, INTERVAL '15 MINUTES')
WHERE event_type = 'DEBIT'
GROUP BY window_start, window_end, user_id;

-- Fraud signal: unusual velocity or large transaction
CREATE MATERIALIZED VIEW fraud_signals AS
SELECT
    ae.event_id,
    ae.user_id,
    ae.amount,
    ae.merchant_name,
    ae.event_time,
    tv.tx_count,
    tv.total_amount,
    CASE WHEN tv.tx_count > 10 THEN 1 ELSE 0 END        AS high_velocity_flag,
    CASE WHEN ae.amount > 2000 THEN 1 ELSE 0 END         AS large_tx_flag,
    CASE WHEN tv.distinct_merchants > 5 THEN 1 ELSE 0 END AS multi_merchant_flag
FROM account_events ae
LEFT JOIN transaction_velocity tv
    ON ae.user_id = tv.user_id
    AND ae.event_time BETWEEN tv.window_start AND tv.window_end
WHERE ae.event_type = 'DEBIT';

User Engagement and Retention Metrics

-- Daily active user (DAU) metrics for product team
CREATE MATERIALIZED VIEW daily_active_users AS
SELECT
    window_start::DATE  AS activity_date,
    COUNT(DISTINCT user_id) AS dau,
    COUNT(DISTINCT session_id) AS total_sessions,
    AVG(session_duration_seconds) AS avg_session_duration,
    COUNT(*) FILTER (WHERE event_name = 'NOTIFICATION_TAP') AS notification_taps
FROM (
    SELECT
        session_id,
        user_id,
        event_name,
        event_time,
        window_start,
        window_end,
        EXTRACT(EPOCH FROM (MAX(event_time) OVER (PARTITION BY session_id) -
                            MIN(event_time) OVER (PARTITION BY session_id))) AS session_duration_seconds
    FROM TUMBLE(user_app_events, event_time, INTERVAL '1 DAY')
) session_data
GROUP BY window_start;

-- Sink fraud signals to notification service
CREATE SINK fraud_notification_queue AS
SELECT
    event_id,
    user_id,
    amount,
    merchant_name,
    event_time,
    (high_velocity_flag + large_tx_flag + multi_merchant_flag) AS fraud_score
FROM fraud_signals
WHERE (high_velocity_flag + large_tx_flag + multi_merchant_flag) >= 2
WITH (
    connector = 'kafka',
    properties.bootstrap.server = 'kafka:9092',
    topic = 'fraud-notifications'
) FORMAT PLAIN ENCODE JSON;

Neobank Data Architecture Comparison

CapabilityTraditional Bank StackNeobank with RisingWave
Transaction notification latencyMinutes to hoursSub-second
Spending insights freshnessMonthly/weekly batchReal-time, per transaction
Fraud detection timingPost-transaction reviewAt-transaction or pre-authorization
Balance accuracyEnd-of-day reconciliationLive running balance
Feature shipping speedMonths (new batch jobs)Days (new SQL views)
Infrastructure teams requiredLarge data engineering teamSmall team, SQL-fluent

FAQ

Q: How do neobanks handle the running balance in RisingWave without it going stale? A: The running_balance field in account_events is maintained by the core banking system and published with each event. RisingWave's materialized view takes the latest running_balance for each account using MAX(event_time) grouping. For the truly real-time balance, the core banking system is the source of truth; RisingWave reflects it within milliseconds of the event being published.

Q: Can RisingWave power push notifications directly? A: RisingWave doesn't push to mobile devices directly, but it sinks fraud signals and notable events to Kafka topics consumed by a notification service (e.g., Firebase Cloud Messaging, AWS SNS). The pipeline: RisingWave detects event → sinks to Kafka → notification service consumes → pushes to mobile. End-to-end latency is typically under 2 seconds.

Q: How do we handle multi-currency accounts in spending summaries? A: Maintain an FX rates CREATE TABLE updated periodically. In spending views, join transactions with FX rates for the transaction date to normalize amounts to the user's home currency before aggregating. Use temporal joins (FOR SYSTEM_TIME AS OF) to get the FX rate valid at transaction time.

Q: What database does RisingWave replace in a neobank stack? A: RisingWave is additive rather than a direct replacement. It sits between your event streams (Kafka) and your serving layer (dashboards, APIs, ML features). It replaces the combination of: a scheduled job runner, a Redis cache for pre-computed aggregates, and a read replica used for analytics queries. The core banking database remains unchanged.

Q: How does RisingWave scale with neobank growth (10x users in 18 months)? A: RisingWave scales horizontally by adding workers. Kafka handles ingestion throughput scaling via partition count. Because materialized views do incremental computation, adding users increases state size linearly but does not increase per-event processing cost. Most neobanks can handle 10x growth with a proportional increase in RisingWave cluster size.


Get Started

Best-in-Class Event Streaming
for Agents, Apps, and Analytics
GitHubXLinkedInSlackYouTube
Sign up for our to stay updated.