Learn

Getting started

Install usecase_forge and execute a typed command through a complete lifecycle.

v0.1.0-dev.2Beginnerusecase_forge
Open source reference

Getting started

This guide creates a counter UseCase. The example is intentionally small, but it still uses the same command, execution context, snapshot, terminal phase, and close lifecycle as a production operation.

Requirements

  • Dart SDK 3.12.2 or newer.
  • A Dart project with a pubspec.yaml.

Install the package

Terminal
shell
dart pub add usecase_forge

Import the public library:

lib/counter_usecase.dart
import 'package:usecase_forge/usecase_forge.dart';

Model state and command

State is an application value. UseCase Forge does not impose a state base class, but meaningful equality prevents redundant publication.

lib/counter_usecase.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({this.by = 1});

  final int by;
}

The command describes intent. It does not execute itself and does not hold UI state.

Register the handler

lib/counter_usecase.dart
final class CounterUseCase extends UseCase<CounterState> {
  CounterUseCase() : super(initialState: const CounterState(0)) {
    registerCommand<Increment>(_increment);
  }

  Future<void> _increment(
    Increment command,
    UseCaseExecutionContext<CounterState> context,
  ) async {
    final current = context.snapshot.state.value;
    context.publish(CounterState(current + command.by));
  }
}

Registration connects one command type to one handler. context.snapshot is the execution-visible snapshot; context.publish moves a new state through the ordered output path.

Execute and close

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

  counter.add(const Increment(by: 2));
  await finished;

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

add submits intent. The returned lifecycle is observed through snapshots and execution handles; close terminates owned resources and must be awaited by the owner.

Next

Read Core concepts to distinguish a command from its execution and a state value from its lifecycle snapshot. Then build the Dart task processor tutorial to apply rate limiting, debounce, grouping, and typed rejection.

ARKTELOS

Purpose-built engineering systems for software that has to hold.