SuperAPI AI Directory

forze-deps-modules

Coding & development

View repository

Updated 10-16 01:19

About this skill

Forze dependency modules

Use when authoring or reviewing infrastructure wiring. For application bootstrap, see forze-wiring. For logical names and StrEnum route values, see forze-specs-infrastructure.

Container model

Deps has two registration modes:

Mode Shape Use when
Deps.plain({DepKey: value}) one provider per key shared clients, secrets, idempotency defaults
Deps.routed({DepKey: {route: value}}) one provider per key + route specs resolved by spec.name
Deps.routed_group({...}, routes={...}) same provider for many routes one backend supports several logical resources

Deps.merge(...) raises CoreError on duplicate plain keys, plain-vs-routed conflicts, or duplicate routed keys. Let it fail fast instead of silently overriding providers.

Module shape

Built-in modules are generic over K: str | StrEnum. Follow that pattern so callers can use StrEnum names.

from enum import StrEnum
from typing import Mapping, final

import attrs

from forze.application.contracts.base import DepKey
from forze.application.execution import Deps, DepsModule


WidgetClientDepKey = DepKey[WidgetClientPort]("widget_client")
WidgetDepKey = DepKey[WidgetDepPort]("widget")


@final
@attrs.define(slots=True, frozen=True, kw_only=True)
class WidgetDepsModule[K: str | StrEnum](DepsModule[K]):
    client: WidgetClientPort
    widgets: Mapping[K, WidgetConfig] | None = None

    def __call__(self) -> Deps[K]:
        plain = Deps[K].plain({WidgetClientDepKey: self.client})
        routed = Deps[K]()

        if self.widgets:
            routed = Deps[K].routed(
                {
                    WidgetDepKey: {
                        name: ConfigurableWidget(config=config)
                        for name, config in self.widgets.items()
                    }
                }
            )

        return plain.merge(routed)

Dep factories

Most spec-backed adapters register factories shaped like:

def __call__(self, ctx: ExecutionContext, spec: WidgetSpec) -> WidgetPort:
    return WidgetAdapter(client=ctx.dep(WidgetClientDepKey), spec=spec, config=self.config)

Application code resolves the factory with route=spec.name, then calls it with (ctx, spec). ExecutionContext convenience methods do this internally for documents, search, cache, counters, storage, locks, and embeddings.

Lifecycle stays separate

Keep DepsModule.__call__ pure and cheap: it builds providers, not network connections. Initialize pools and clients in LifecycleStep functions (postgres_lifecycle_step, redis_lifecycle_step, s3_lifecycle_step, etc.).

Routed infrastructure

For tenant-aware clients, register a structural client port as a plain dependency and let routed clients choose the concrete connection at call time. For routed Postgres clients, pass introspector_cache_partition_key when catalog metadata differs by tenant/database.

Anti-patterns

  1. Instantiating adapters directly in usecases — use deps factories and ports.
  2. Using only strings in new modules — keep route type as K: str | StrEnum.
  3. Opening connections in __call__ — lifecycle owns startup/shutdown.
  4. Returning overlapping keys from multiple modules — compose maps before module construction or use distinct routes.
  5. Registering spec-backed providers as plain deps when multiple specs exist — use routed deps keyed by spec.name.

Reference