Files
baya-monorepo/product/data-model/12-audit-config-and-reference.md
T
hamid 2f2aec61a2 backend phase 1: config, reference & platform signals
Lay the cross-cutting platform backbone every later phase reads from. Adds
the first marketplace EF migration baseline (new `ops` schema) and the
mechanisms b2..b15 reuse: typed runtime config, an append-only audit trail,
an analytics event log, the holiday/bank-closure calendar, in-app
notifications, and the internal support-alert worklist.

Schema & migration
- New `ops` schema + migration InitialMarketplaceBaseline with 6 tables:
  PlatformConfigs (IAuditable), AuditLogs (append-only), SystemEvents,
  IranianHolidays, Notifications, SupportAlerts — with indexes/uniques and
  FKs to usr.Users. Seeded 12 config keys + 7 sample holidays via HasData.

Domain / Application
- IAuditable marker + [AuditRedacted] attribute; entities + string-code
  constant holders (config data_type, holiday type, alert type/severity/status).
- Facade contracts: IPlatformConfig, IHolidayCalendar, IAnalyticsSink,
  IAuditLogger, INotificationService, ISupportAlertService; DTOs +
  PagedResult<T>; evolved the INotificationDispatcher.Notification record to
  carry Type + DataJson; Pagination helper.
- 14 CQRS commands/queries (+ validators) wiring the endpoints to the facades.

Infrastructure
- DB-backed facade implementations in Persistence/Services/; real in-app
  INotificationDispatcher (removes the b0 log stub); notification-retention
  hosted service (purge is_read=1 AND age>90d).
- Extended AuditFieldInterceptor to also append an old/new-diff audit_logs row
  for every IAuditable change in the same transaction (PII redacted).
- Registered all facades + hosted service in AddPersistenceServices; removed
  the dispatcher registration from AddCrossCuttingSeams.

API
- 5 controllers: admin PlatformConfig/Holidays/Audit/SupportAlerts
  ([Authorize(DynamicPermission)]) + current-user Notifications ([Authorize]),
  all tenant-scoped and paginated. 16 Swagger paths total.

Money-correctness & safety rules honoured
- Config read at compute time (cached, parsed by data_type), never hardcoded;
  every config change is audited in the same transaction; audit_logs is
  append-only (no update/delete path); support alerts are admin-only;
  notifications are tenant-scoped; analytics is fire-and-forget.

Tests & docs
- 18 new foundation tests over in-memory SQLite (config typing + audit,
  holidays, notifications + tenancy + retention, support alerts, analytics);
  build clean (0 new code warnings), 22 tests green; migration applied to the
  dev DB and swagger.v1.json refreshed.
- Updated server Project map + CONVENTIONS, product data-model doc 12 (seeded
  config defaults), config-reference contract, mock registry, backend handoff/
  STATUS/report.

Follow-ups: add FK constraints for SupportAlerts.BookingId (b9) and ReviewId
(b14) when those tables land.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 01:18:00 +03:30

3.7 KiB
Raw Blame History

Domain 12 — Audit, Config & Reference

← Database Model

audit_logs [CORE]

Role: Immutable, append-only record of every state change on sensitive entities — now explicitly including platform_configs (so finance can prove the commission rate at any moment). Why: compliance and accountability; changed_fields_json enables fast filtering. Plan month-partitioning + 23yr cold-storage archival before launch. Fields unchanged. Relations: polymorphic, append-only.

system_events [MVP]

Role: High-volume behavioral/analytics event log. Why kept but de-emphasized: product analytics, not compliance. It grows unbounded — at scale, pipe it to an analytics sink/warehouse rather than the transactional DB. Fields unchanged.

platform_configs [CORE]

Role: Key-value runtime business parameters — change without a deploy. Why typed values: data_type tells the app how to parse. New keys this revision: dispute_window_hours (default 72), vat_rate (0.10), bnpl_merchant_of_record, bnpl_provider_commission_rate, bnpl_settlement_timing, cancellation-tier defaults — alongside the existing platform_fee_rate, booking_payment_deadline_minutes, nurse_response_deadline_hours, nurse_payout_interval_days, evv_location_tolerance_meters, min_rating_for_support_alert. Relations: referenced everywhere; changes audited.

Seeded defaults (as built, backend-phase-1). The baseline migration seeds every key below. Values marked provisional were chosen as safe defaults where the product docs did not pin a number — confirm before launch; each is config-driven so it changes without a deploy.

Key data_type Seeded value Source
platform_fee_rate decimal 0.15 provisional
vat_rate decimal 0.10 doc (10%, commission line only)
dispute_window_hours int 72 doc
booking_payment_deadline_minutes int 30 doc
nurse_response_deadline_hours int 24 provisional
nurse_payout_interval_days int 7 doc (weekly)
evv_location_tolerance_meters int 200 provisional
min_rating_for_support_alert decimal 2 provisional (review ≤ 2 raises an alert)
bnpl_merchant_of_record string platform doc (Balinyaar is MoR)
bnpl_provider_commission_rate decimal 0.07 provisional
bnpl_settlement_timing string immediate provisional
cancellation_tiers json [{"min_hours_before":48,"refund_percent":100},{"min_hours_before":24,"refund_percent":50},{"min_hours_before":0,"refund_percent":0}] provisional

Rates are DECIMAL fractions (not money); the IRR amounts they later multiply are BIGINT. Read them at compute time (cached via IPlatformConfig), never hardcode, and snapshot the rate used onto the priced booking/invoice so a later rate change never re-prices an existing row.

iranian_holidays [MVP] — NEW

Role: Shared official/religious holiday calendar (movable, partly lunar-Hijri), with a is_bank_closed flag. Why a real table: Iran's holidays are numerous and partly movable, and they drive payout bank-closure scheduling (PAYA/SATNA closed → a weekly payout shifts to the next business day), optional holiday pricing, and business-hour deadline math — none of which a purely manual per-nurse availability exception can express.

Field Type Notes
id BIGINT PK
holiday_date DATE
name_fa NVARCHAR(200)
type NVARCHAR(20) official / religious / national
is_bank_closed BIT Drives payout date shifting

Relations: referenced by payout scheduling and (optionally) pricing.