Design Notes for dxaws-dns¶
This document captures the design intent and architectural rules for the
dxaws-dns primitive.
The module is intentionally small: it exists to provide a stable, composable contract for managing DNS record sets in AWS Route 53 using declarative convergence.
Architectural Position in dxaws¶
dxaws-dns is a primitive module.
That means:
It exposes stable Python contracts.
Its public interfaces are immutable and only extendable.
It contains no orchestration beyond converging a single DNS record.
It is designed to be composed by higher-level modules (e.g., website/CDN orchestration).
In the dxaws stack, the rough layering looks like:
dxaws-core: shared session, provider base classes, o11y
dxaws-dns: DNS record primitives (this module)
higher-level modules: orchestration (CloudFront, websites, multi-account)
Core Pattern: Declarative Convergence¶
The core workflow is:
Normalize desired (complete inputs; canonicalize)
Read current (from provider)
Plan (diff desired vs current)
Apply (execute the plan)
Return a result with an outcome:
noopif no changes were requiredappliedif AWS was mutated
The contract goal is deterministic behavior:
Apply once →
appliedApply again →
noopIntroduce drift →
appliedRe-apply →
noop
This is validated via AWS-backed acceptance tests.
Layering¶
Models (contracts)¶
Models define the stable API surface and must not leak AWS/boto3 shapes.
DnsRecordDesiredDnsRecordCurrentDnsRecordPlanDnsManagerResult
Contract notes:
zone_nameis the human-facing identifier and is always required.zone_idis optional at call sites and may be resolved by the system.
Manager (orchestration + normalization)¶
The manager owns:
Input normalization (canonicalization)
Workflow orchestration (current → plan → apply)
Emitting o11y events (when enabled)
Normalization rules live here because:
It keeps the planner deterministic.
It prevents duplication across call sites (CLI, tests, orchestrators).
Planner (pure diff)¶
The planner:
Takes
(desired, current)Produces a plan
Must be side-effect free (no AWS calls)
Executor (mutation)¶
The executor:
Takes a plan
Calls provider write operations
Must not perform input enrichment (e.g., resolving
zone_id)
Provider (AWS isolation)¶
The provider is the only layer that should:
Touch boto3
Encode AWS/Route 53 quirks
The provider surface should stay small and explicit.
Canonicalization Rules¶
These are the canonicalization rules that make diffs stable and idempotency real.
Hosted zone identity¶
Callers provide:
zone_name(e.g.,dev.dxaws.com)
The system may resolve:
zone_idviaprovider.resolve_zone_id(zone_name=...)
This keeps AWS implementation details out of call sites.
Record names¶
Record names are normalized to a fully-qualified name based on zone_name.
Example:
record_name="_test"+zone_name="dev.dxaws.com"becomes
"_test.dev.dxaws.com."
Record types¶
Record types are normalized to uppercase.
TXT values (quoting)¶
Route 53 returns TXT values wrapped in double quotes.
To keep diffs stable:
Desired TXT values are normalized to quoted form.
Provider write operations also normalize to quoted form.
This ensures that a second apply becomes noop.
CNAME targets¶
Route 53 stores DNS names with a trailing dot.
Callers should prefer canonical targets (e.g., example.com.). The planner
operates on the normalized values.
Acceptance Tests as Contract Tests¶
Acceptance tests in this module are treated as contract tests.
They validate:
Create / read / delete lifecycle
Idempotency (
noopon second apply)TTL drift detection + correction
Multiple record types (TXT, A, CNAME)
Safety features:
Tests are opt-in (
DXAWS_AWS_TESTS=1).Tests require explicit zone confirmation (
DXAWS_ACCEPTANCE_CONFIRM).Tests print AWS identity before mutation.
Tests clean up after themselves and verify deletion.
Tradeoffs and Intentional Limitations¶
This module focuses on record sets, not zone provisioning.
It avoids complex routing policies (weighted, latency, geo) until the core contracts are proven.
It intentionally prefers explicit, composable APIs over convenience wrappers.
If/when the module expands, it should do so by extending interfaces rather than changing existing fields or semantics.
Extensibility Guidelines¶
When extending this module:
Prefer adding new optional fields to
DnsRecordDesiredover changing meaning.Keep planner logic deterministic and side-effect free.
Put AWS quirks in the provider and canonicalization in the manager.
Add acceptance coverage for any new record type or behavior.