Skip to main content

dxaws-dns / design

·644 words·4 mins

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:

  1. Normalize desired (complete inputs; canonicalize)
  2. Read current (from provider)
  3. Plan (diff desired vs current)
  4. Apply (execute the plan)
  5. Return a result with an outcome:
    • noop if no changes were required
    • applied if AWS was mutated

The contract goal is deterministic behavior:

  • Apply once → applied
  • Apply again → noop
  • Introduce drift → applied
  • Re-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.

  • DnsRecordDesired
  • DnsRecordCurrent
  • DnsRecordPlan
  • DnsManagerResult

Contract notes:

  • zone_name is the human-facing identifier and is always required.
  • zone_id is 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_id via provider.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 (noop on 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 DnsRecordDesired over 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.