Skip to content

Address deliverability — the tier-1 verification contract

Geocoding tells you an address plausibly exists — there’s a building there on the map. It does not tell you the address is deliverable — that a courier or the postal service will actually get a parcel to it. That stronger answer comes from an authoritative address-verification provider (Smarty, Loqate, Google Address Validation), each backed by postal-authority data (DPV / RDI and the like).

Granit.AddressDeliverability.Abstractions is the contract for that check — tier 1 of the address platform. It ships abstractions only. No concrete provider is in the framework baseline; a host opts in by registering a provider package, and until then the enrichment orchestrator simply skips the tier. This lets modules and the orchestrator depend on deliverability without taking a hard dependency on any vendor.

public interface IAddressDeliverabilityService
{
Task<AddressDeliverabilityResult> CheckAsync(Address address, CancellationToken ct = default);
}
public sealed record AddressDeliverabilityResult(
AddressDeliverabilityOutcome Outcome,
Address? Standardized = null, // the corrected address, when the provider fixed it
IReadOnlyList<string>? Flags = null, // provider classification flags (DPV, residential, vacant…)
string? ProviderMatchCode = null); // the provider's raw match code
public enum AddressDeliverabilityOutcome
{
Verified, // deliverable as submitted
Corrected, // deliverable after the provider standardized it
Unverifiable, // no authoritative match — neither valid nor invalid
Invalid, // not deliverable
}

Standardized lets a provider hand back a cleaned-up address (canonical casing, expanded abbreviations, corrected postcode) that the caller can choose to adopt; Flags surface provider-specific classifications; ProviderMatchCode is the raw code, carried into the verification verdict’s Evidence for provenance.

Why “Deliverability”, not “Verification”

Section titled “Why “Deliverability”, not “Verification””

The name is deliberate, and avoids two collisions:

  • The persisted verdict value object is AddressVerification. Naming this package Granit.AddressVerification would put a namespace on top of that type, shadowing it in every consumer that uses both.
  • The framework’s input-validation family is Granit.Validation* (FluentValidation-based, syntactic). Deliverability is a different thing — an authoritative real-world check, not a format rule.

“Deliverability” names the provider check (a delivery-point validation), cleanly distinct from the verification verdict it contributes to and from format validation.

A provider package implements IAddressDeliverabilityService, maps its vendor’s response onto AddressDeliverabilityResult, and registers the service. Once registered, Granit.AddressEnrichment picks it up automatically as its tier-1 step — no other wiring.

public sealed class AcmeDeliverabilityService(/* vendor client */) : IAddressDeliverabilityService
{
public async Task<AddressDeliverabilityResult> CheckAsync(Address address, CancellationToken ct)
{
var vendor = await /* call the vendor */;
return new AddressDeliverabilityResult(
Outcome: Map(vendor.Status),
Standardized: vendor.Corrected is { } c ? Address.Create(/* … */) : null,
Flags: vendor.Flags,
ProviderMatchCode: vendor.Code);
}
}