did-btcr2-js

ADR 024: API Facade: Lazy Construction and Layered Configuration

Status: Accepted

Date: 2026-04-10

Commit: 8fe1404

Context

@did-btcr2/api is the high-level SDK façade that downstream CLI and application code uses to drive @did-btcr2/method. It composes several sub-façades (crypto, did, kms, btc, method, cas), each of which carries its own construction cost: Helia boot, Bitcoin RPC client, KMS backend, etc.

Two earlier attempts each had problems:

  1. Eager construction of everything up front. Worked but forced every consumer (including a CLI that just wants to resolve a DID) to pay for Helia boot, Bitcoin connection, KMS init, etc. on every invocation. Slow, wasteful, and failure-prone (one subsystem misconfiguration blocks the whole façade).
  2. Manual composition by consumer code. Every consumer had to know how to wire every sub-façade, which defeated the point of having a façade.

Separately, the CLI needed configuration from multiple sources: CLI flags, env vars, config files, built-in defaults: with clear precedence and validation. Ad-hoc merging in each command was producing inconsistent behavior.

Options considered

  1. Eager construction, factored into builders. Better factoring but still eager.
  2. Lazy construction via getters. Sub-façades instantiate on first access. Composes naturally with layered config because configuration can be fully resolved before any sub-façade asks for it.
  3. Dependency injection container. Overkill for a façade; would obscure the composition story.

Decision

Option 2 for API; a small typed layered-config module for CLI.

Lazy API construction. Api exposes sub-façades as property getters. Each getter instantiates the underlying component on first access and caches it. Consumers that never touch api.btc never boot a Bitcoin connection.

Layered config (CLI). packages/cli/src/config.ts implements a strict precedence:

CLI flags  >  env vars  >  config file  >  built-in defaults

Each layer produces a partial ResolvedConfig; layers merge by deep-override. The resolved config is validated once at startup and passed to Api.

Consequences

Positive

Negative

Explicitly accepted trade-offs

References