usecase_forge 0.1.0-dev.5 copy "usecase_forge: ^0.1.0-dev.5" to clipboard
usecase_forge: ^0.1.0-dev.5 copied to clipboard

A pure Dart command-driven UseCase runtime with typed handlers, scheduling policies, cancellation, snapshots, and bounded history.

UseCase Forge #

Russian translation

English is the primary language of the ecosystem. README.ru.md is a supplementary Russian translation.

UseCase Forge is a pure Dart, command-driven UseCase runtime. It accepts typed commands, resolves input and processing policies, runs exact-type handlers, publishes immutable snapshots, supports cooperative cancellation, and retains bounded terminal history.

Pre-release: the API may still change before 1.0. Pin the version and review the changelog when upgrading.

Core has no Flutter or RxDart dependency. Version 0 targets the Dart VM; Dart Web is not supported yet.

Features #

  • Exact runtime-type command handlers.
  • Concurrent execution by default, with opt-in sequential lanes.
  • Input conflict, debounce, throttle, and sliding rate-limit policies.
  • Stable policy configuration through UseCase defaults and exact-type registration; individual add call sites cannot change orchestration.
  • Processing policies: coexist, restart, and reject-new.
  • Synchronous state publication with equality-based deduplication.
  • Cooperative cancellation without pretending to abort a Dart Future.
  • Awaited lifecycle hooks and explicit error stages.
  • Replay-latest broadcast snapshot stream.
  • Bounded terminal execution history.
  • Optional injected clock for deterministic policy and timestamp tests.
  • Automatic read-only Dart VM diagnostics for the separate DevTools package in supported debug runs, with application values hidden by default.

Installation #

Add the package dependency:

dependencies:
  usecase_forge: ^0.1.0-dev.5
dart pub add usecase_forge:^0.1.0-dev.5

Ecosystem packages #

Package Purpose
usecase_forge_flutter Provider, Builder, Listener, Selector, Consumer, ownership, and context lookup for Flutter
usecase_forge_test Deterministic clocks, observers, matchers, and lifecycle test helpers
usecase_forge_devtools Read-only VM diagnostics in a DevTools extension

All companion packages are published independently. None of them is a core runtime dependency.

Minimal example #

import 'package:usecase_forge/usecase_forge.dart';

final class CounterState {
  const CounterState(this.value);

  final int value;

  @override
  bool operator ==(Object other) =>
      other is CounterState && other.value == value;

  @override
  int get hashCode => value.hashCode;
}

final class Increment extends UseCaseCommand {
  const Increment();
}

final class CounterUseCase extends UseCase<CounterState> {
  CounterUseCase() : super(initialState: const CounterState(0)) {
    registerCommand<Increment>((command, context) async {
      context.publish(CounterState(context.snapshot.state.value + 1));
    });
  }
}

Future<void> main() async {
  final counter = CounterUseCase();
  final finished = counter.stream.firstWhere(
    (snapshot) => snapshot.phase == UseCaseExecutionPhase.finished,
  );

  counter.add(const Increment());
  await finished;

  print(counter.state.state.value); // 1
  await counter.close();
}

Run the complete version:

dart run example/usecase_forge_example.dart

The examples index also includes catalog search, document synchronization, rate limiting and conflict policies, cooperative cancellation, command-specific error recovery, error propagation and hooks, and history diagnostics.

DevTools diagnostics #

The separate usecase_forge_devtools package provides the developer-facing DevTools tab. Core stays pure Dart: it has no Flutter or third-party runtime dependency and exposes only a small read-only bridge through dart:developer in supported non-product Dart VM runs.

The bridge reports registered command types, the current snapshot, Admission, Pending, Starting, Processing and Finalizing entries, terminal history, rejections, cancellation and error-routing events. It cannot add commands, cancel work, change State or replay executions.

State, commands, execution keys, reasons, errors and stack traces are hidden by default. To reveal selected JSON-safe fields, import the secondary library and mix in UseCaseDiagnosticsDataProvider:

import 'package:usecase_forge/diagnostics.dart';

final class CartUseCase extends UseCase<CartState>
    with UseCaseDiagnosticsDataProvider {
  // Normal UseCase implementation omitted.

  @override
  String get useCaseDiagnosticLabel => 'Shopping cart';

  @override
  Object? encodeUseCaseDiagnosticValue(
    UseCaseDiagnosticValueKind kind,
    Object? value,
  ) {
    if (kind == UseCaseDiagnosticValueKind.state && value is CartState) {
      return {'itemCount': value.itemCount};
    }
    return null; // Keep every other value hidden.
  }
}

Returned data is validated, copied and bounded. Formatter failures are contained by diagnostics and never enter UseCase.onError, the snapshot stream or terminal history. See the complete runnable example and the diagnostics contract.

Execution model #

UseCase.add returns void. The runtime creates an execution entry, passes it through the input pipeline, schedules it, and exposes progress through state, stream, and terminal history.

Instructions are configured on the UseCase instance and on exact command-type registration. Defaults are frozen lazily for the instance; resolved registration instructions are reused by every command of that type. If an operation needs different execution semantics, model it as a different command type or a different UseCase rather than changing an individual add call.

Execution is concurrent by default. Global state follows the order of synchronous context.publish calls, not command submission or future completion order. Use sequential lanes when operations must observe ordered state.

Policies that relate executions require a non-null UseCaseCommand.executionKey. Relation identity combines the exact command runtime type with that key.

Cancellation #

context.cancel() immediately finalizes the execution as cancelled and frees its processing slot. Dart futures cannot be forcibly aborted, so the handler should observe context.isCancellationRequested or await context.whenCancellationRequested where appropriate. A late publish throws UseCaseExecutionAlreadyFinishedException and is routed through onError without rewriting history.

State equality #

Snapshots are deduplicated with ==. State classes must implement meaningful value equality. Equality that is too broad can hide updates; identity-only equality can emit unnecessary updates.

Errors and hooks #

Hooks return Future<void> and may observe a lifecycle step, but they cannot replace its Entry or result. Handler and hook errors pass through onError. The default implementation forwards the original error to the snapshot stream. See the lifecycle contract and public API contract.

An exact command registration can add a typed onError callback. It receives the command, live execution context, original error, and original stack trace. Return UseCaseCommandErrorAction.handled after command-specific recovery or failure publication. Return UseCaseCommandErrorAction.useDefault to continue through the global UseCase.onError flow unchanged. See the command error handling example.

Public API #

The main library exports the UseCase facade, commands and handlers, execution context and value objects, snapshots, instructions, error context, rejection reasons, and documented exceptions. Normal consumers should use package:usecase_forge/usecase_forge.dart, not direct imports from lib/src/. The only secondary public library in v0 is package:usecase_forge/diagnostics.dart, used for explicit DevTools data opt-in.

Testing and controlled time #

Production UseCases normally call super(initialState: ...) and use the system clock. A test may instead pass a public UseCaseClock implementation through super(initialState: ..., clock: clock). The same clock controls debounce, throttle and rate-limit timers plus createdAt, queuedAt, startedAt and finishedAt. The separate usecase_forge_test package provides TestUseCaseClock, an event-recording observer, semantic matchers and the useCaseTest helper.

This clock is a testability boundary, not a public view of private queues. Admission, pending and processing internals remain encapsulated.

Documentation and project #

UseCase Forge is created and maintained by Petr Orlov and published through the verified ArkTelos publisher. For package and ecosystem questions, contact packages@arktelos.dev or use the protected ArkTelos contact form.

Report suspected vulnerabilities through the private process described in SECURITY.md, not in a public issue.

License #

Licensed under the Apache License, Version 2.0. Copyright 2026 Orlov Petr Petrovich. See LICENSE for the license terms and NOTICE for attribution.

Dependencies and development tools keep their own licenses; the Apache 2.0 license applies to UseCase Forge code and does not relicense third-party work.

0
likes
160
points
218
downloads

Documentation

Documentation
API reference

Publisher

verified publisherarktelos.dev

Weekly Downloads

A pure Dart command-driven UseCase runtime with typed handlers, scheduling policies, cancellation, snapshots, and bounded history.

Homepage
Repository (GitLab)
View/report issues

Topics

#architecture #concurrency #state-management #use-case

License

Apache-2.0 (license)

More

Packages that depend on usecase_forge