Files
zarinpal-analytics-backend/docs/decisions/0007-calendar-aware-analytics-projections.md

31 lines
2.8 KiB
Markdown

# ADR 0007: Calendar-aware analytics projections without initial table partitioning
- Status: Accepted
- Date: 2026-08-20
## Context
The source contains approximately 2.2 million payment-attempt rows, but attempts are only one domain entity. Merchant, terminal, session, attempt, import, identity, and reporting-date concepts require separate normalized relations connected by constraints. The product needs daily, weekly, Jalali-month, and Persian-season analysis.
Calculating every dashboard result from normalized attempt rows on every request creates avoidable latency. Conversely, splitting attempts into unrelated tables by month/category or adding PostgreSQL partitions before measuring query and maintenance behavior introduces complexity without evidence. Some metrics are additive across days, while distinct payer-token counts and percentiles are not.
## Decision
Keep normalized business/source-of-truth relations: categories, merchants, merchant users, terminals, payment sessions, payment attempts, and import provenance. The attempt relation retains one row per source attempt; normalization does not arbitrarily distribute a single entity's rows across category/month tables.
Add a reporting-date model containing Gregorian and Jalali date attributes, explicit reporting-week boundaries, Persian months/seasons, and weekdays.
Build idempotent, rebuildable analytics-section queries containing additive components such as session count, attempt count, successful count, amount sum, and fee sum as approved by the metric catalogue. Derive week/month/season totals through the reporting-date model and recompute ratios from their numerators and denominators.
Calculate non-additive metrics such as exact unique payer-token counts directly at their requested grain; never obtain them by summing daily distinct counts. Cache each typed section in Redis under the immutable completed-import snapshot and metric-definition version.
Keep normalized fact tables unpartitioned initially. Add targeted indexes from approved API access paths and verified query plans. Introduce PostgreSQL partitioning only through a future ADR supported by measured table growth, query latency, vacuum/maintenance behavior, or retention operations.
## Consequences
- The normalized ERD remains semantically correct while dashboards reuse disposable Redis results.
- Section calculations support approved period groupings without storing calculated facts as PostgreSQL business truth.
- Explicit reporting-period definitions and metric provenance make future AI observations traceable.
- Lazy parallel calculation, dataset-versioned Redis keys, job deduplication, partial failure, and SSE visibility tests become required behavior.
- Schema/index/cache details remain subject to data profiling and approved metric/API requirements.