daemon split (8/8): document capability decoupling + wireServices

Update ARCHITECTURE.md's Composition section to reflect the finished
split: capabilities carry explicit service-pointer fields, nothing
reaches *Daemon at dispatch time, and wireServices(d) is the single
entry point that builds services + capabilities eagerly (from Open
in production, from tests after constructing &Daemon{...} literals).

Removes the paragraph admitting capability→*Daemon coupling and the
lazy-init getters justification, neither of which applies anymore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Thales Maciel 2026-04-21 15:59:39 -03:00
parent 9c73155e17
commit 011b59a72f
No known key found for this signature in database
GPG key ID: 33112E6833C34679

View file

@ -41,15 +41,20 @@ consumer-defined seams:
declaring a function-typed interface for every call would balloon
the surface for no win — services are unexported, so package-external
code can never reach them.
- Capability hooks still take `*Daemon` as their receiver argument,
but `VMService` calls into them through a `capabilityHooks` struct
(function-typed bag) populated at construction. The service has no
`*Daemon` pointer.
- Capability hooks do not take `*Daemon`. Each capability is a struct
with explicit service-pointer fields (`workDiskCapability{vm, ws,
store, defaultImageName}`, `dnsCapability{net}`, `natCapability{vm,
net, logger}`) populated at wiring time. `VMService` invokes them
through a `capabilityHooks` struct (function-typed bag) populated at
construction; neither the service nor any capability has a `*Daemon`
pointer.
Lazy-init getters (`d.hostNet()`, `d.imageSvc()`, `d.workspaceSvc()`,
`d.vmSvc()`) let existing test literals (`&Daemon{store: db, runner: r}`)
keep working — the getter constructs the service from whatever is on
the `Daemon` if nothing was pre-wired.
Services + capabilities are built eagerly by `wireServices(d)`, called
once from `Daemon.Open` after the composition root's infrastructure is
populated, and once per test that constructs a `&Daemon{...}` literal.
Tests that want to stub a particular service or the capability list
assign the field before calling `wireServices` — the helper is
idempotent and skips anything already set.
## Service state