Skip to content

CapabilityScope

What Is a Scope?

A CapabilityScope is the container that manages all capability compositions. It holds two registries (composers and compositions) and provides the entry points for composing and querying capabilities.

Creating a Scope

csharp
// Default — both registries enabled
var scope = new CapabilityScope();

// With options
var scope = new CapabilityScope(new CapabilityScopeOptions
{
    UseComposerRegistry = false,
    UseCompositionRegistry = true
});

Composing Capabilities

Compose() creates a Composer for a given subject:

csharp
var composer = scope.Compose("my-subject");
composer.Add(new SomeCapability()).Build();

This registers the subject in the composer registry (if enabled), then Build() stores the resulting composition in the composition registry.

If you call Compose() on a subject that already has a composition, the new composition replaces the existing one in the registry. Use Recompose() instead when you want to build on existing capabilities.

Subject lifetime

When using a reference-type subject, the scope stores it via ConditionalWeakTable — if the subject is garbage collected, the composition silently disappears. Always keep a reference to your subject:

csharp
// Bad — temporary object, no reference held
scope.Compose(new MyService()).Add(cap).Build(); // composition may vanish after GC

// Good — reference kept alive
var service = new MyService();
scope.Compose(service).Add(cap).Build(); // composition lives as long as service

See Registries for details on reference vs value type storage.

Recomposing

Use Recompose() to create a new composition based on an existing one — it inherits all capabilities from the original:

csharp
var existing = scope.Compositions.GetRequired<string>("my-subject");
scope.Recompose(existing)
    .Add(new AnotherCapability())
    .Build();

The new composition replaces the old one in the registry. The difference from calling Compose() again: Recompose() carries over all existing capabilities, while Compose() starts from scratch.

Context Isolation

Different scopes are completely independent worlds:

csharp
var scopeA = new CapabilityScope();
var scopeB = new CapabilityScope();

scopeA.Compose("user").Add(new CapA()).Build();
scopeB.Compose("user").Add(new CapB()).Build();

// scopeA only has CapA for "user"
// scopeB only has CapB for "user"

Scope Lifetime

Scopes are typically long-lived — created once and shared. Common patterns:

  • Application-wide: One scope for the entire app
  • Per-tenant: Isolated scope per tenant
  • Per-test: Fresh scope per test for isolation

CapabilityScope implements IDisposable to clean up registry resources.

Properties

PropertyTypeDescription
ComposersComposerRegistryApiAccess the composer registry
CompositionsCompositionRegistryApiAccess the composition registry
OwnerScopeOwnerApiAssociate a single owner with the scope
AnchorsScopeAnchorsApiAssociate typed or named anchors with the scope

Scope Flow

Released under the Apache-2.0 License.