Documentation
¶
Overview ¶
Package capability defines GoCell capability providers: assembly-level shared infrastructure resources (postgres pool, redis client, ...) provisioned once by the composition root and injected into every consuming cell module.
Why assembly-level (not per-cell) ¶
A capability is a fx.Supply-shaped shared value, not a per-cell constructor. The composition root provisions postgres pool(s) / one redis client and injects them into every consuming cell module via the sealed PGSet resolver:
- Colocated topology: one pool serves all cells; every ForCell call returns the same PGProvider (Sole() reports true). This preserves the single-pool
- LIFO-shutdown invariant and the one-outbox-table / one-relay model.
- Split topology (#2341): N distinct-DSN pools are provisioned, one per distinct DSN group. Each cell module calls ForCell(cellID) to obtain its own pool's PGProvider; Sole() returns (nil, false). One relay per pool drains that pool's outbox traffic.
Redis is always single-instance (no per-cell Redis today). See ADR docs/architecture/202605251500-adr-capability-provider-interface.md.
Layering: runtime/capability never imports adapters/ ¶
runtime/ may depend only on kernel/ + pkg/ (CLAUDE.md). So the provider interfaces are expressed over kernel types (persistence.TxRunner, outbox.Writer) plus a deliberate `any` typed-erasure seam (DB() / Client()) for the raw adapter handle. The `any` is type-asserted exactly once, in the consumer site inside cmd/* (which may import adapters/).
Sealed construction (AI-robust: upstream Hard) ¶
PGProvider / RedisProvider carry an unexported marker method, so they can only be implemented inside this package. The concrete impls (pgProvider / redisProvider) are private; NewPGProvider / NewRedisProvider are the sole construction paths. A cell module cannot fabricate a bypass provider — it must consume the injected one. This is the upstream-Hard half of the funnel: Go's type system makes a forged provider unrepresentable outside this package.
Funnel closure (downstream Medium) ¶
The downstream half is archtest CAPABILITY-PROVIDER-FUNNEL-01 (tools/archtest/capability_provider_funnel_test.go): a type-aware caller allowlist that bans the shared-infrastructure constructors (adapters/postgres.NewPool / NewTxManager / NewOutboxWriter, adapters/redis.NewClient) anywhere except the single provisioning site cmd/corebundle/cap_wiring.go. Per-cell / per-role derivations that take an already-acquired handle (NewSessionStore / NewLedgerStore / NewOutboxStore / NewCache / NewRedisDriver / …) are intentionally NOT banned.
Rating: upstream Hard (sealed) + downstream Medium (archtest caller-allowlist, not compile-time) — an allowed transition form per ai-robust.md §"Funnel 双向锁评级". The downstream→Hard upgrade is tracked at gh issue #988. The blind-spot inventory lives in that archtest's package godoc, not duplicated here.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Kind ¶
type Kind string
Kind names an assembly-level shared infrastructure capability. Cells declare what they consume via cell.yaml `requires`; the assembly's provisioned set is the derived union of its cells' requires (Design Y, #855 — see kernel/assembly.GenerateModulesGen). The closed value set below is mirrored by the cell.schema.json `requires` enum (kept in lockstep by kernel/metadata/schemas TestSchemaConstantsMatchSchemaLiterals). Unknown or duplicate values are rejected by `gocell validate` governance rule FMT-36 at validation time — not at parse time: ParseFS stays lenient, matching Kubernetes admission-layer enum validation rather than parse-time rejection.
const ( // Postgres is the shared postgres pool (TxManager + OutboxWriter + DB). Postgres Kind = "postgres" // Redis is the shared redis client. Redis Kind = "redis" // RabbitMQ is recognized by the enum but has NO provider / provisioning path // yet: there is no RabbitMQProvider and cap_wiring's provisionCapabilities has // no rabbitmq case, so declaring `requires: [rabbitmq]` in a cell.yaml passes // FMT-36 + codegen but fails fast at provisionCapabilities' default branch. It stays in // the enum as recognized forward vocabulary; a real AMQP-shared-connection // assembly must land the RabbitMQProvider + provisioning atomically. RabbitMQ Kind = "rabbitmq" )
type PGInstance ¶
type PGInstance struct {
Provider PGProvider
Cells []string
}
PGInstance pairs one pool's provider with the cells it serves. The composition root builds one per distinct-DSN pool (cellmodules/percellpg.Resolve groups the cells); cells across instances must be disjoint.
type PGProvider ¶
type PGProvider interface {
// TxManager returns the shared transactional runner bound to the pool.
TxManager() persistence.TxRunner
// OutboxWriter returns the transactional outbox writer.
OutboxWriter() outbox.Writer
// DB returns the raw pool handle (*adapterpg.Pool.DB()) as any. The single
// type-assertion lives in the cmd/* consumer; runtime/capability stays adapter-free.
DB() any
// contains filtered or unexported methods
}
PGProvider is the sealed handle to the assembly's single postgres pool. It is implementable only inside package capability (unexported marker isPGProvider); the sole construction path is NewPGProvider. Consumers receive an injected PGProvider and must not construct adapter primitives themselves (CAPABILITY-PROVIDER-FUNNEL-01).
func NewPGProvider ¶
func NewPGProvider(tx persistence.TxRunner, writer outbox.Writer, db any) PGProvider
NewPGProvider wraps the composition-root-constructed postgres primitives into the sealed PGProvider. tx and writer are kernel-typed; db is the raw *adapterpg.Pool.DB() handle passed as any so this package never imports adapters/. Called only from the assembly wiring site (cmd/<id>/cap_wiring.go).
type PGSet ¶
type PGSet interface {
// ForCell returns the postgres provider for cellID, or a fail-closed error if
// the cell has no provisioned pool (never a silent nil).
ForCell(cellID string) (PGProvider, error)
// Sole returns (provider, true) iff the assembly is colocated (exactly one
// distinct pool serving every cell). In split topology it returns (nil, false).
// It is the SANCTIONED single accessor for assembly-wide consumers that have no
// "cell" dimension — notably the CQRS projection harness, whose journal
// global_seq is per-pool and therefore incomparable across a split fan-out, so
// it MUST fail closed when Sole reports false.
Sole() (PGProvider, bool)
// contains filtered or unexported methods
}
PGSet is the per-cell postgres provider resolver injected on composition.SharedDeps (#2341). It replaces the former single SharedDeps.PG PGProvider so each cell module resolves ITS pool's provider via ForCell(cellID) — colocated assemblies map every cell to one shared provider (one pool); split assemblies map each cell to its own pool's provider (N pools, each driven by its own relay keyed by InfraInstanceKey).
It is sealed (unexported marker isPGSet; sole constructor NewPGSet) so the per-instance fan-out cannot be forged outside the composition root, and ForCell fails closed on an unknown cell rather than returning a silent nil. memory topology leaves SharedDeps.PG nil — cell modules take their in-memory path.
func NewPGSet ¶
func NewPGSet(instances []PGInstance) (PGSet, error)
NewPGSet builds the sealed per-cell provider resolver from the per-instance providers. It fails closed on a wiring bug — a nil provider, an instance serving no cells, or a cell mapped to two pools — rather than building a half-wired set. Sole() reports true iff there is exactly one instance (colocated). Called only from the assembly wiring site (cmd/<id>/cap_wiring.go).
A nil or empty instances slice is valid by design: it produces an empty set where every ForCell call returns an error (fail-closed) and Sole() returns (nil, false). This is the memory-topology path where no postgres pool is provisioned; cell modules take their in-memory repository path instead.
type RedisProvider ¶
type RedisProvider interface {
// Client returns the raw redis client (*adapterredis.Client) as any. The
// single type-assertion lives in the cmd/* consumer.
Client() any
// contains filtered or unexported methods
}
RedisProvider is the sealed handle to the assembly's shared redis client. Implementable only inside package capability; sole construction path NewRedisProvider.
func NewRedisProvider ¶
func NewRedisProvider(client any) RedisProvider
NewRedisProvider wraps the composition-root-constructed redis client into the sealed RedisProvider. client is the raw *adapterredis.Client passed as any.