ADR-0001. CDC-driven tax provisioning with idempotent reconcile
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-04-01 |
| Deciders | taxation-team |
| Supersedes | — |
Context
- Commerce owns
Product(including itstaxGroupId); taxation must not call commerce synchronously on every product write. - Tax must stay consistent as products are created, change tax group, lose their tax group, or get deleted.
- Debezium already streams
public.Productchanges; reusing that stream avoids a new API contract. - At-least-once delivery + consumer restarts mean the same change may be processed more than once.
Decision
We consume the Debezium public.Product topic in a single ApplicationKafkaComponent and route every change to TaxationWorkerService.handleProductCDC, which reconciles rather than applies deltas: c/u/r → provision when taxGroupId is set (else deprovision), d → deprovision. Provisioning is idempotent — TaxProvisioningService skips when an ACTIVATED TaxSet already exists with sourceType = TaxGroup and a matching sourceId.
Consequences
| Pros | Cons |
|---|---|
| No synchronous coupling to commerce | No backfill: fallbackMode: latest skips pre-offset changes |
| Reconcile is self-healing on redelivery | DEACTIVATED TaxSets accumulate (never hard-deleted) |
| Single topic, single consumer — minimal surface | No DLQ; onMessageError only logs |
Idempotency key (sourceType, sourceId) avoids duplicate TaxSets | Single hardcoded Snowflake worker id blocks horizontal scale |
Alternatives Considered
| Option | Pros | Cons | Why rejected |
|---|---|---|---|
| Commerce calls taxation REST on product write | Immediate, no Kafka | Sync coupling; commerce must know taxation; ret//retry complexity | Couples write path; CDC already available |
| Delta-based handling (apply only the diff) | Less work per message | Fragile under redelivery / out-of-order | Reconcile is simpler and self-healing |
fallbackMode: earliest + backfill | Catches historical rows | Replays entire history on every group reset | Operationally heavy; manual provision endpoints suffice |
References
packages/taxation/src/components/kafka/component.tspackages/taxation/src/services/taxation-worker.service.tspackages/taxation/src/services/tax-provisioning.service.ts:provisionForProductpackages/core/src/common/kafka/topics.ts:CDCKafkaTopics.PRODUCT