Skip to main content

dart_subsystem 0.1.2 copy "dart_subsystem: ^0.1.2" to clipboard
dart_subsystem: ^0.1.2 copied to clipboard

A lightweight, lifecycle-safe framework to manage singleton subsystems and services in Dart applications.

example/example.dart

import 'package:dart_subsystem/dart_subsystem.dart';

// 1. Define Category/Type descriptor
final networkTypeDesc = SubsystemTypeDesc(
  displayName: 'Network Services',
  description: 'Services managing communication and network I/O',
  serviceTypeId: 'core.network',
);

// 2. Define concrete Subsystem implementation descriptor
final apiClientClassDesc = SubsystemClassDesc(
  serviceType: networkTypeDesc,
  displayName: 'API Client Subsystem',
  description: 'Manages HTTP/REST calls and connection state',
  serviceClassId: 'network.api_client',
  defaultBuilder: (params) async => ApiClientSubsystem(classDesc: params.classDescription),
);

// 3. Subsystem implementation
class ApiClientSubsystem extends Subsystem {
  ApiClientSubsystem({required super.classDesc});

  bool _isConnected = false;
  bool get isConnected => _isConnected;

  @override
  void beginConstruct(BuildServiceParameters params) {
    // Synchronous setup right after instantiation
    print('[ApiClient] Constructing subsystem...');
  }

  @override
  Future<void> initialize(BuildServiceParameters params) async {
    // Asynchronous loading or handshake
    print('[ApiClient] Connecting to remote service...');
    await Future<void>.delayed(const Duration(milliseconds: 50));
    _isConnected = true;
    print('[ApiClient] Connected successfully!');
  }

  void fetchData(String endpoint) {
    // Protect against usage after disposal
    assertNotDisposed();
    print('[ApiClient] Fetching data from: $endpoint');
  }

  @override
  Future<void> dispose() async {
    // Release resources, disconnect connections
    print('[ApiClient] Disconnecting...');
    _isConnected = false;
  }

  @override
  Future<void> finalDispose() async {
    // Final cleanup guaranteed to execute even if dispose throws
    print('[ApiClient] Final cleanup complete.');
  }
}

// --- Shared-type, multi-implementation example -----------------------------
//
// A shared package only ever references [notificationsTypeDesc]. It has no
// idea which concrete class each consuming app registers behind it — that's
// entirely up to the app. This is what decouples a shared package from any
// single app's implementation.

final notificationsTypeDesc = SubsystemTypeDesc(
  displayName: 'Notifications',
  description: 'Delivers local or push notifications to the user',
  serviceTypeId: 'core.notifications',
);

abstract class NotificationsSubsystem extends Subsystem {
  NotificationsSubsystem({required super.classDesc});
  void notify(String message);
}

// App "mobile" registers a local-notifications implementation...
class LocalNotificationsSubsystem extends NotificationsSubsystem {
  LocalNotificationsSubsystem({required super.classDesc});
  @override
  void notify(String message) => print('[Local] $message');
}

// ...while app "web" would register a completely different one, behind the
// same type (not registered in this example, shown for illustration only).
class WebPushNotificationsSubsystem extends NotificationsSubsystem {
  WebPushNotificationsSubsystem({required super.classDesc});
  @override
  void notify(String message) => print('[WebPush] $message');
}

final localNotificationsClassDesc = SubsystemClassDesc(
  serviceType: notificationsTypeDesc,
  displayName: 'Local Notifications',
  description: 'OS-level local notifications',
  serviceClassId: 'notifications.local',
  defaultBuilder: (params) async => LocalNotificationsSubsystem(classDesc: params.classDescription),
);

// --- Capability interface + global lookup -----------------------------------
//
// `allInstancesOfClass`/`allInstancesWhere` scan every registered type, so a
// generic "boot participant" style interface can be implemented by subsystems
// of any type, without the caller enumerating types manually.

abstract class BootParticipant {
  void onBootCompleted();
}

class LoggingSubsystem extends Subsystem implements BootParticipant {
  LoggingSubsystem({required super.classDesc});

  @override
  void onBootCompleted() => print('[Logging] Boot completed, ready to log.');
}

final loggingTypeDesc = SubsystemTypeDesc(
  displayName: 'Logging',
  description: 'Application-wide logging sink',
  serviceTypeId: 'core.logging',
);

final loggingClassDesc = SubsystemClassDesc(
  serviceType: loggingTypeDesc,
  displayName: 'Logging Subsystem',
  description: 'Writes logs to stdout',
  serviceClassId: 'logging.stdout',
  defaultBuilder: (params) async => LoggingSubsystem(classDesc: params.classDescription),
);

Future<void> main() async {
  // Optional: hook into lifecycle errors during disposal
  SubsystemInstanceRegistry.onLifecycleError = (error, stackTrace) {
    print('[Lifecycle Error] $error');
  };

  // Build and register subsystems using the factory
  final factory = TSubsystemFactory();
  factory.addServices([
    BuildServiceParameters(classDescription: apiClientClassDesc),
    BuildServiceParameters(classDescription: localNotificationsClassDesc),
    BuildServiceParameters(classDescription: loggingClassDesc),
  ]);

  print('Registering subsystems...');
  await factory.registerSubsystems();

  // Retrieve subsystem instance synchronously
  final client = apiClientClassDesc.getInstance<ApiClientSubsystem>();
  client?.fetchData('/users');

  // The caller only knows the type here, not the concrete class registered above.
  for (final notifier in notificationsTypeDesc.allInstancesOfType<NotificationsSubsystem>()) {
    notifier.notify('Hello!');
  }

  // Global, cross-type lookup by capability interface.
  for (final participant in SubsystemInstanceRegistry.allInstancesOfClass<BootParticipant>()) {
    participant.onBootCompleted();
  }

  // Predicate-based lookup, e.g. every instance belonging to a specific type category.
  final networkInstances = SubsystemInstanceRegistry.allInstancesWhere(
    (instance) => instance.classDesc.serviceType.serviceTypeId == 'core.network',
  );
  print('Found ${networkInstances.length} network subsystem(s).');

  // Teardown
  print('Tearing down subsystems...');
  await SubsystemInstanceRegistry.clearAll();
  print('Done!');
}
1
likes
160
points
361
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A lightweight, lifecycle-safe framework to manage singleton subsystems and services in Dart applications.

Repository (GitHub)
View/report issues

Topics

#dart #subsystem #management #lifecycle #service

License

MIT (license)

Dependencies

meta

More

Packages that depend on dart_subsystem