docs: define calendar-aware analytics model

This commit is contained in:
2026-08-20 13:38:23 +03:30
parent f91e529298
commit 9d57be161d
4 changed files with 127 additions and 2 deletions

View File

@@ -13,6 +13,8 @@ Repeatably ingest the supplied payment-attempt archive into PostgreSQL, preserve
- Streamed `.csv.gz` ingestion from a local, Git-ignored input path
- Import runs, checksums, schema version, row counts, rejection/error reporting
- Normalized merchant, category, terminal, payment-session, payment-attempt, and account/profile concepts
- Versioned Gregorian/Jalali calendar dates and curated Iranian event/holiday periods
- Rebuildable daily base summaries and metric-specific period projections for approved analytics
- Synthetic merchant display profiles and one demo account per merchant
- Idempotent re-runs and explicit import lifecycle
- Database constraints/indexes driven by documented access paths
@@ -36,6 +38,10 @@ erDiagram
MERCHANT ||--o{ PAYMENT_SESSION : receives
TERMINAL ||--o{ PAYMENT_SESSION : handles
PAYMENT_SESSION ||--|{ PAYMENT_ATTEMPT : contains
CALENDAR_DATE ||--o{ CALENDAR_DATE_EVENT : includes
CALENDAR_EVENT ||--o{ CALENDAR_DATE_EVENT : occurs_on
CALENDAR_DATE ||--o{ MERCHANT_DAY_SUMMARY : groups
MERCHANT ||--o{ MERCHANT_DAY_SUMMARY : summarizes
IMPORT_RUN ||--o{ PAYMENT_SESSION : introduced_by
IMPORT_RUN ||--o{ PAYMENT_ATTEMPT : introduced_by
IMPORT_RUN ||--o{ IMPORT_ISSUE : reports
@@ -111,6 +117,34 @@ erDiagram
bigint affected_rows
jsonb evidence
}
CALENDAR_DATE {
date date PK
int jalali_year
int jalali_month
int jalali_day
text persian_season
int iso_weekday
}
CALENDAR_EVENT {
bigint id PK
text code UK
text title_fa
text event_type
text source_version
}
CALENDAR_DATE_EVENT {
date date FK
bigint calendar_event_id FK
}
MERCHANT_DAY_SUMMARY {
bigint merchant_id FK
date local_date FK
bigint session_count
bigint attempt_count
bigint successful_count
bigint amount_sum
bigint source_import_run_id FK
}
```
This ERD is a logical baseline, not an approved physical schema. In particular, source key types, card-key retention, analytics projections/materialized views, and whether session-level fields can conflict across retries must be resolved from a profiling report before migrations are approved.
@@ -124,6 +158,7 @@ This ERD is a logical baseline, not an approved physical schema. In particular,
5. Validate uniqueness of `(session_key, try_seq)`, referential consistency, parseability, null rates, ranges, timestamp ordering, and merchant/terminal/category relationships.
6. Promote accepted data transactionally or through a resumable, explicitly versioned workflow; a failed run cannot appear complete.
7. Emit a machine-readable and human-readable import report. Do not log raw card keys or credentials.
8. Populate and validate the approved calendar range, then rebuild affected analytics projections idempotently after source promotion. Projection failure must not make an incomplete projection appear fresh.
## Synthetic merchant/account rules
@@ -143,6 +178,9 @@ This ERD is a logical baseline, not an approved physical schema. In particular,
- Missing values remain missing rather than being silently replaced with factual-looking defaults.
- The import report captures checksum, importer revision, timings, counts, validation failures, and quality metrics.
- Raw data and database state remain ignored by Git; code, schema, rules, docs, and safe templates are tracked.
- Daily, Jalali-month, Persian-season, and approved calendar-event analytics reconcile to deterministic source queries for additive metrics.
- Rates are recomputed from their numerator and denominator over the requested period rather than averaged from daily percentages.
- Non-additive metrics such as unique payer-token counts are never produced by summing daily distinct counts.
## Approval gates / unresolved decisions
@@ -158,6 +196,6 @@ Resolved: retain `payer_card_key` unchanged as a restricted, merchant-scoped opa
Resolved: peer benchmarks exclude the active merchant and require at least 10 other merchants plus 1,000 combined peer sessions in the approved cohort/window. Otherwise the benchmark is suppressed as insufficient data; the merchant's own analytics remain available. See ADR 0006.
Remaining approval gates:
Resolved: use normalized source-of-truth entities plus versioned calendar/event dimensions, rebuildable daily base summaries, and metric-specific projections for non-additive or measured high-cost period analytics. Monthly, Persian-season, and holiday results use explicit calendar definitions. Keep fact tables unpartitioned initially and introduce partitioning only from measured need. See ADR 0007.
1. Physical partitioning/materialization strategy, which must follow measured query needs rather than the logical ERD alone.
No broad architecture approval gates remain for this capability. Physical columns, indexes, constraints, calendar source, projection shapes, and migration details still require profiling evidence and acceptance in the implementation-ready revision of this spec.

View File

@@ -0,0 +1,32 @@
# 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 calendar concepts require separate normalized relations connected by constraints. The product also needs daily, Jalali-month, Persian-season, and Iranian holiday/event analysis, including evidence that may later support bounded AI recommendations.
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 versioned calendar model containing Gregorian and Jalali date attributes, Persian seasons, weekdays, and curated named events such as Nowruz. Calendar-event definitions have explicit provenance/version and are deterministic inputs to analytics, not LLM-generated labels.
Build idempotent, rebuildable daily base projections containing additive components such as session count, attempt count, successful count, amount sum, and fee sum as approved by the metric catalogue. Derive month/season/event totals by grouping these components through the calendar model and recompute ratios from their numerators and denominators.
Create separate metric-specific period projections only for non-additive metrics or query paths proven expensive—for example, exact unique payer-token counts cannot be obtained by summing daily distinct counts. Projection rows record source import/version and freshness.
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 read smaller, purpose-built projections.
- Daily components support many calendar groupings without precomputing every possible period.
- Calendar definitions and metric provenance make future AI observations traceable.
- A single observed Nowruz period may be reported as an observation but not claimed as recurring seasonality; multiple comparable years are needed for that claim.
- Projection refresh, failure, stale-state visibility, and reconciliation tests become required operational behavior.
- Schema/index/projection details remain subject to data profiling and approved metric/API requirements.