Введение
M5.2 закрыл фундаментальный слой — ITGC. Это инфраструктура: access, change, ops, SDLC. Они защищают всю среду, но не знают про business-семантику конкретного CDE. Если ваш commission engine считает payouts неправильно из-за бага в формуле, никакой Snowflake RBAC не поможет — инженер с легитимным доступом записал bad logic, всё прошло через CI, и неверные значения уверенно протекли в driver_earnings_ledger.
Application controls — это слой контролей, сидящий поверх ITGC, специфичный для business logic. Они вступают в действие во время обработки данных: на input, во время processing, на output. Commission engine SwiftRide — пайплайн на ~25 dbt-моделей — нуждается в application controls на каждом из этих трёх этапов.
В этом уроке разберём три категории — input, processing, output — на конкретных примерах из пайплайна расчёта driver earnings SwiftRide. Под конец увидим компромисс: при сильных ITGC application controls становятся primary audit-relevant defense (PCAOB AS 2201 ¶.47 «lower risk» классификация); при слабых ITGC application controls не убеждают аудитора сами по себе.
Категория 1 — Input controls
Цель: блокировать invalid, malformed или out-of-domain data до того, как они попадут в source-of-truth.
Schema enforcement
Базовый input control — schema validation. Пайплайн отказывается принимать запись, если schema не соответствует.
В реализации SwiftRide:
- Kafka topic
trips.completed— Avro schema через Confluent Schema Registry. Compatibility check BACKWARD включён; producer, пытающийся подать несовместимую schema, получает 4xx response. - dbt source contract на
raw.trips— column types + non-null constraints + uniqueness. dbt-контракт fails build при schema drift. - PostgreSQL
tripstable — строгие типы (DECIMAL(10,2) наfare_total), check constraints, foreign keys.
Range / domain validation
После schema check — value-level validation. Schema говорит «fare_total это DECIMAL»; range check говорит «между 5000».
Примеры SwiftRide на trips:
fare_total_cents >= 0 AND fare_total_cents <= 500000(max $5000 per trip).commission_pct >= 0.0 AND commission_pct <= 0.5(max 50% commission, регуляторный потолок).country_code IN (whitelist на 40 стран).trip_started_at < trip_completed_at.trip_completed_at <= now() + interval '1 hour'(поездки с датой в будущем — ошибка).
Реализации:
- Database CHECK constraints (PostgreSQL).
- dbt
accepted_values,not_null, custom tests. - GE Core 1.17.1 expectation suite — запускается на каждом batch.
Type validation / sanitization
Кроме schema — семантическая валидация.
Примеры:
email_addressсоответствует regex pattern + DNS-MX check.phone_numberсоответствует E.164 format + country prefix матчится сcountry_code.vat_numberсоответствует per-country format (VIES validation).ip_addressвалидный IPv4 / IPv6.
Специфика PII — input validation также является соответствием GDPR Art. 5(1)(c) («data minimisation») — не принимать data вне периметра.
Completeness controls (на стороне input)
Обнаруживают missing batches / частичные loads.
Примеры:
- Daily batch приходит — record count соответствует ожидаемому (в пределах толеранса ±2% против trailing 7-day average).
- Hourly Kafka offset committed — детекция gap per partition (alert если consumer lag больше 5 минут).
- Source-system file load — checksum match против producer-published manifest.
Категория 2 — Processing controls
Цель: гарантировать, чтобы transformation от raw input к canonical output была корректной и без silent corruption.
Reconciliation (в полёте)
Reconciliation между промежуточными стадиями.
Пайплайн driver earnings SwiftRide:
trips.completedKafka →stg_trips(dbt staging) — count match (в пределах ±1% — толеранс late-arriving).stg_trips→int_trip_aggregates(dbt intermediate) — sum check: total fare в staging = total fare в intermediate.int_trip_aggregates×commission_rules→fct_driver_earnings— formula spot-check: 100 random rows recompute formula, ожидается match.
Каждый шаг reconciliation имеет собственную log entry в S3 object lock + immutable evidence.
Double-entry / parity
Заимствовано из accounting principle. Каждая транзакция записывается дважды, и накопленные balances должны match.
SwiftPay wallet SwiftRide:
- Каждый payout —
debitк pool account +creditк driver account. Daily reconciliation: sum(debits) = sum(credits). Любая разница → ledger imbalance, Sev-1.
Не каждый CDE требует double-entry, но любой financial CDE — да.
Formula validation
Критично для calculation-пайплайнов.
Расчёт commission SwiftRide:
- Production formula хранится в version-controlled config (
commission_rules.yaml). - Spot-check pipeline: для каждого batch выбирает 100 случайных
trip_id, перевычисляет earnings через canonical Python implementation (отдельный codepath), сравнивает с dbt model output. Mismatch больше 0 row → Sev-1. - Почему две реализации? Защита от бага в логике dbt model. Если SQL имеет subtle bug (rounding, type coercion), Python check ловит. См. K019 — это reference-вариант parity check.
Sequence / ordering controls
Для event-sourced пайплайнов — обеспечить обработку событий по порядку.
Kafka consumer SwiftRide:
- Каждое событие имеет monotonic
sequence_idpertrip_idpartition key. - Consumer отбрасывает события с lower seq, чем уже обработанные (идемпотентность).
- Out-of-order events логируются + reviewed (могут указывать на clock skew у источника).
Категория 3 — Output controls
Цель: обеспечить, чтобы final output (CDE table, regulatory file, BI dashboard feed) был accurate, complete и timely.
Cross-system reconciliation
Самый важный output control — подробно покрыт в M5.6.
Концепция: независимое сравнение final output между двумя независимыми источниками.
Примеры SwiftRide:
fct_driver_earnings.gross_earnings_usd(Snowflake daily aggregate) противswiftpay.payouts.amount_paid_usd(Aurora actual payouts, после обработки) — daily delta больше 0.05% триггерит инцидент.fct_revenue_daily.net_revenue_usd(Snowflake) против Treasury cash-in (банковский источник, ежемесячно) — monthly delta больше 0.1% триггерит расследование.kyc_profile.status='approved'count (Snowflake) против Onfido provider count (внешняя истина) — еженедельный анализ delta.
Threshold alerts
Output-значения против ожидаемых диапазонов; alert при выходе за границы.
SwiftRide:
- Daily
net_revenue_usd< 10M → operations review (типично $6-8M). - Daily
kyc_approvals_countdrop больше 40% против trailing 30-day average → fraud / outage investigation. driver_earnings.payout_per_driveroutlier больше $10K per day → manual review.
Completeness controls (на стороне output)
Проверяют, что ожидаемый output произведён.
fct_revenue_daily— 6 БЮ × 40 стран = 240 ожидаемых partition rows per day. Row count <230 → completeness check fails.- IRS 1099 export — ожидается ~50K eligible drivers; фактический count в пределах ±2% оценки.
Timeliness controls
Output произведён в пределах SLA.
fct_driver_earningsдля day T должен завершиться к T+1 06:00 UTC. Airflow DAG SLA fail → PagerDuty.- KYC approval decision в течение 24h после подачи — count pending больше 24h → Sev-3 alert.
Distribution / access controls
Output controls также включают, куда output идёт.
- IRS 1099 export — S3 bucket с restricted access (Tax team + Finance Lead); SHA-256 signed; зашифрованный передаётся IRS-партнёру по email.
- Driver Payouts Dashboard в Looker — RBAC на уровне БЮ; PII fields замаскированы без явного grant.
Driver earnings SwiftRide — полный набор контролей
Контроли input / processing / output встроены в пайплайн. Каждая ячейка — конкретный control + evidence artifact.
Пайплайн имеет 13 application controls. Каждый имеет evidence artifact: dbt run artifacts, GE Data Docs, reconciliation logs в S3 object lock, Airflow run logs, PagerDuty incidents, Looker audit logs.
Компромиссы — ITGC против application controls
Когда строить ITGC против application control для покрытия риска?
| Аспект | ITGC | Application control |
|---|---|---|
| Периметр | Cross-CDE (purpose-agnostic) | CDE-specific (или семейство CDE) |
| Стоимость | High initial; амортизируется по всем CDE | Per-CDE; может масштабироваться медленно |
| Стоимость audit-test | Тестируется один раз, используется много раз | Тестируется per material control |
| Время реакции | Часто медленное (UAR квартально, change CR ежедневно) | Быстрое (real-time, per-record) |
| Периметр сбоя | Failure cascades — все CDE на этой инфре затронуты | Failure локализован в пределах одного семейства CDE |
| Оптимальное применение | Фундамент; auth, deploy, ops, lifecycle | Корректность business-logic, per-CDE |
PCAOB AS 2201 ¶.47 следствие: при effective ITGC application controls «generally expected to be lower risk» — audit testing сокращается.
Практическое правило:
- Сначала строим сильный ITGC. Без effective ITGC application controls не могут получить lower-risk классификацию под AS 2201 ¶.47.
- Затем наслаиваем application controls. Per material CDE. Используем M5.1 defense-in-depth (минимум 1 preventive + 1 detective + желательно corrective).
- Не подменяем одно другим. ITGC не закрывает business logic risk; application controls не закрывают infrastructure risk. Они комплементарны.
BCBS 239 Principle 3 — accuracy and integrity
BCBS 239 Principle 3 требует largely automated aggregation, минимизированных manual workarounds, reconciliation to source. На языке SwiftRide — это автоматизированные input + processing + output controls, с reconciliation evidence.
BIS BCBS d559 (November 2023) implementation review — из 31 G-SIBs только 2 fully compliant. Principle 3 (accuracy) — повторяющаяся слабость: банки выполняют DQ checks, но без traceable reconciliation chain. SwiftRide должен начинать с traceable chain reconciliation как day-1 requirement для material CDE.
Anti-patterns
Input validation в downstream вместо upstream
Паттерн: валидация происходит в dbt staging layer вместо на источнике (PostgreSQL CHECK или Kafka schema). Bad data попадает в raw layer, ловится позже.
Почему плохо: больше latency catch; bad data может протечь в других consumers raw-слоя.
Fix: валидируем на источнике (PostgreSQL CHECK constraints, Kafka Schema Registry). Staging layer повторяет validation как defense-in-depth.
Processing без reconciliation между стадиями
Паттерн: пайплайн staging → intermediate → mart, но никаких sum/count checks между стадиями.
Почему плохо: silent corruption в intermediate layer (например, JOIN, дропающий rows из-за NULL key) не обнаруживается.
Fix: каждый переход между стадиями имеет как минимум count + sum reconciliation log entry; immutable storage.
Output controls только в production, не в pipeline runs
Паттерн: cross-system reconciliation запускается только после завершения пайплайна; если пайплайн производит wrong data, reconciliation ловит — но wrong data уже visible в Looker-дашборде в течение 6 часов.
Почему плохо: latency window между production и detection — material exposure.
Fix: reconciliation в pipeline DAG как gating step; downstream consumers получают output только после прохождения reconciliation.
Чрезмерная опора на один тип control
Паттерн: «у нас daily reconciliation — все CDE покрыты».
Почему плохо: daily reconciliation = detective only. Input bug + reconciliation = damage caught after damage done.
Fix: defense-in-depth (M5.1). Каждый material CDE имеет input preventive (schema + range) + processing preventive (formula validation) + output detective (cross-system reconciliation).
Резюме
- Application controls в 3 категориях: input (schema, range, type, completeness), processing (reconciliation in-flight, double-entry, formula parity, sequence), output (cross-system reconciliation, threshold alerts, completeness, timeliness, distribution).
- Пайплайн driver_earnings SwiftRide — 13 application controls. Каждый с evidence artifact (dbt run, GE Data Docs, reconciliation log, Airflow log, PagerDuty incident, Looker audit).
- ITGC против application controls: комплементарны, не взаимозаменяемы. Периметр ITGC cross-CDE; application controls CDE-specific. PCAOB AS 2201 ¶.47 — effective ITGC даёт lower-risk классификацию application controls.
- BCBS 239 Principle 3 — accuracy and integrity — требует largely automated aggregation + reconciliation to source. BIS d559 review: только 2 из 31 G-SIBs fully compliant.
- Сначала строим сильный ITGC (M5.2). Затем наслаиваем application controls per material CDE (M5.4 — декомпозиция objective/activity/evidence).
В M5.4 разберём подробно — как разложить «control» на objective + activity + evidence; почему «SELECT COUNT(*) > 0» не является control.
Измерение completeness — input controls Выявление нарушений DQ — processing controls