ADR-0002. TaxInfo is authoritative, sourced from Merchant CDC — not commerce metadata.tax
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-04-08 |
| Deciders | invoice-team, commerce-team |
| Supersedes | — |
Context
- Merchant tax identity (MST: taxCode, fullName, address, administrative codes) is entered in
@nx/commerceasMerchant.metadata.tax. - The invoice service needs a normalized, queryable, relation-backed tax record (
tax.TaxInfo) for issuance payloads and FE display. metadata.taxis free-form jsonb on a foreign service — unsuitable as the authoritative record the invoice domain reads from.- Debezium's before-image is unreliable for change detection (depends on the table's REPLICA IDENTITY).
Decision
We will treat metadata.tax as input only and tax.TaxInfo (principalType=Merchant) as the authoritative record.
InvoiceWorkerService.handleMerchantCDC consumes the Merchant CDC topic; TaxInfoService.syncTaxInfo upserts TaxInfo. On op=u it diffs the incoming tax against the persisted TaxInfo (not the Debezium before-image) and skips when unchanged. The frontend reads the merchant.taxInfo relation, never metadata.tax.
Consequences
| Pros | Cons |
|---|---|
| Single authoritative, relation-backed tax record | Eventual consistency — depends on CDC lag |
| Change detection correct regardless of REPLICA IDENTITY | Extra read (load persisted TaxInfo) per update |
| FE/issuance decoupled from commerce jsonb shape | Two representations of "tax" exist (input + authoritative) |
| Idempotent re-delivery (unchanged → skip) | Requires Debezium pipeline to be healthy |
Alternatives Considered
| Option | Pros | Cons | Why rejected |
|---|---|---|---|
Read metadata.tax directly at issuance | No CDC | Cross-service jsonb coupling; no normalization | Brittle; FE can't relation-join |
| Trust Debezium before-image for diffs | No extra read | Unreliable without FULL replica identity | Causes false skips/updates |
| Sync HTTP call to commerce per issuance | Always fresh | Latency + availability coupling | Defeats event-driven design |
References
src/services/invoice-worker.service.ts(handleMerchantCDC,_resyncMerchantTaxInfoIfChanged,_isTaxInfoUpToDate)src/services/tax-info.service.ts(syncTaxInfo)packages/core/src/models/schemas/tax/tax-info/schema.ts- See also: Integration §3.1