ADR-0001. Two provider-adapter layers: @nx/iiapi and @nx/t-van
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-04-01 |
| Deciders | invoice-team |
| Supersedes | — |
Context
- Vietnamese e-invoicing reaches the tax authority through different channels: a direct e-invoice provider (VNIS, VNPAY) and a transaction-VAN (T-VAN) gateway.
- The two channels have different SDK shapes, auth models, and credential storage (per-merchant vs single platform connection).
- The DB encodes its own provider enum (
InvoiceProviders), which must not leak the iiapi enum (IIAPIProviders) into persistence. - Credentials differ per channel: VNIS uses a platform
Configurationrow; VNPAY uses per-merchantInvoiceProviderrows; T-VAN uses a platformConfigurationrow.
Decision
We will wrap each channel in its own adapter package and register them with separate components:
@nx/iiapi(VNIS + VNPAY) registered byInvoiceProviderConnectionComponent.@nx/t-van(T-VAN gateway, VNPAY provider) registered byTVanConnectionComponent.
The DB↔iiapi provider enums are bridged by InvoiceProviderMapper (src/common/providers.ts), which throws on unknown providers. Persistence only ever stores InvoiceProviders values.
Consequences
| Pros | Cons |
|---|---|
| Each channel evolves independently | Two registration components + boot paths to maintain |
| Persistence decoupled from vendor enums | Mapper must be updated when a new provider is added |
| Per-merchant vs platform credential models cleanly separated | Only VNPAY is mapped today — others throw until added |
| Adding a provider = extend mapper + adapter, no schema change | Two credential-loading paths (Configuration row vs provider row) |
Alternatives Considered
| Option | Pros | Cons | Why rejected |
|---|---|---|---|
| Single adapter for all channels | Fewer components | Forces one SDK shape onto incompatible APIs | Channels too different (direct vs VAN) |
| Store iiapi enum directly in DB | No mapper | Vendor enum leaks into persistence; migration churn | Couples schema to a third-party package |
| Per-merchant T-VAN config | Symmetric with VNPAY | T-VAN is a single platform gateway today | Over-engineered for current need |
References
src/common/providers.ts(InvoiceProviderMapper)src/components/invoice-provider-connection/component.tssrc/components/tvan-connection/component.tssrc/migrations/data/configuration.ts(VNIS_DEFAULT_CONNECTION,TVAN_DEFAULT_CONNECTION)