5.2 KiB
ADR 0010: Separate the core facade from optional integration modules
Status
Accepted. Partially supersedes ADR 0003 as described below. Partially superseded by ADR 0011 for the consumer-migration consequence during the unreleased prototype transition. It also supplies the registration and invocation-lifetime decisions left open here. The module boundary and remaining principles continue to apply; this record's decision text is retained.
Date
2026-10-09
Context
CommandKit serves consumers that need its command infrastructure with different combinations of library integrations, including none. A consumer using only the core should not acquire dependencies belonging to optional integrations.
ADR 0003 establishes deliberate public contracts and an internal CLI backend boundary. Those principles remain appropriate. Its initial single root facade, however, combines core and integration responsibilities. Separate packages within one module would separate imports while retaining shared module requirements.
Decision
Retain the root commandkit package as the core module's public facade. It owns
core command declarations, registration, typed inputs, dispatch, help/version,
shared syntax, diagnostics, and invocation outcomes.
Provide optional library integrations as separate Go modules with their own public package contracts. Each integration owns the standard command handlers, provider contracts, reports, and adapters appropriate to its responsibility. Applications select integrations and explicitly register their commands; importing an integration does not install a command family.
Integrations provide convenient registration of their standard command families, with standard declarations and handlers supplied by the integration. Applications provide acquisition and domain-specific context without assembling individual standard commands or repeating adapter plumbing. More specialized composition remains available where concrete consumer needs justify it.
Dependencies point from integrations toward the core. The core's public API, implementation, and module requirements remain independent of optional integration libraries. An integration may depend on another integration when its responsibility requires that relationship. Document such dependencies so consumers can understand the modules they select.
Integrations compose through supported public core contracts and preserve CommandKit's standard grammar, command ownership, help, and outcome conventions. They do not access private core state or backend structures. Add public contracts where concrete integration needs justify them, without requiring a general plugin or backend-selection framework.
Relationship to ADR 0003
The root-facade and public-type ownership provisions of ADR 0003 now apply to the core module. Integration-owned public types and operations belong in their respective public packages. The permission to expose deliberately selected upstream library types applies within the integrations that depend on them; it does not justify optional integration dependencies in the core.
ADR 0003's remaining principles continue to apply across the core and integrations: deliberate exports, private implementation state, proportionate internal layering, no exposure of internal or backend types, and preservation of documented ownership, presence, callback lifetime, and error semantics. Application input types and caller-supplied values and errors retain their existing handling. Cobra/pflag remain behind the core's internal adapter.
This decision changes the scope of the single-facade decision rather than replacing its contract discipline. ADR 0003 is partially superseded and its original decision text remains intact.
Alternatives considered
| Alternative | Analysis |
|---|---|
| Keep integrations in the root package | Preserves existing imports and simplifies releases, but core consumers acquire optional library dependencies. |
| Use separate packages within one module | Separates compiled imports and retains one release unit, but keeps integration requirements in the shared module graph. |
| Have applications implement integration handlers | Keeps the core independent, but recreates duplicated inspection commands and weakens consistent operator behavior. |
| Introduce universal provider contracts in the core | Could support several libraries through a common model, but adds abstractions and conversion obligations before concrete needs justify them. |
Consequences
Consumers can adopt the core independently and add supported integrations as needed. Integrations remain maintained parts of CommandKit, with shared CLI conventions and owning-library semantics. Dependency independence does not mean the core has no external implementation dependencies.
Moving existing integration APIs requires a documented consumer migration. Integration modules add module-specific dependency, validation, and release work. Core tests, examples, and tools must not reintroduce the optional dependencies that this boundary removes.
Exact registration mechanics and release cadence are not selected here. Current GoDoc and CLI contracts continue to own implemented behavior.