buildInstances<TSubsystem extends Subsystem> static method

Future<List<TSubsystem>> buildInstances<TSubsystem extends Subsystem>(
  1. List<BuildServiceParameters> paramsList, {
  2. bool atomicBatch = false,
})

Builds and initializes a batch of subsystems defined by paramsList.

Synchronous subsystems (BuildServiceParameters.isSynchronous = true) are initialized first sequentially to make foundational services available to asynchronous ones.

Partial failure behavior: If a subsystem fails during initialization or post-initialization, only the failing instance is rolled back (disposed and removed from the registry). Subsystems that were successfully initialized earlier in the batch remain active in the registry. Set atomicBatch to true to roll back all instances from the batch on any failure.

If an error occurs during initialization, the failed instance is disposed, removed from the registry, recorded in lastFailedRegistrations, and a SubsystemInitializationException is thrown.

Implementation

static Future<List<TSubsystem>> buildInstances<TSubsystem extends Subsystem>(
  List<BuildServiceParameters> paramsList, {
  bool atomicBatch = false,
}) async {
  final instances = <TSubsystem, BuildServiceParameters>{};
  final asyncInitInstances = <TSubsystem>[];

  // Phase 1: Construction
  for (final params in paramsList) {
    if (await params.shouldBuild == false) {
      continue;
    }

    TSubsystem? instance;
    try {
      instance = await _buildInstance<TSubsystem>(params);
    } catch (error, stackTrace) {
      _failedRegistrations.add(params);
      if (atomicBatch) {
        await _rollbackBatch(instances);
      }
      throw SubsystemInitializationException(
        message: 'Failed to construct subsystem',
        parameters: params,
        cause: error,
        stackTrace: stackTrace,
      );
    }

    if (instance != null) {
      instances[instance] = params;

      if (params.isSynchronous) {
        try {
          await instance._initSubsystem(params);
        } catch (error, stackTrace) {
          _failedRegistrations.add(params);
          await _cleanupFailedInstance(instance, params);
          if (atomicBatch) {
            await _rollbackBatch(instances);
          }
          throw SubsystemInitializationException(
            message: 'Failed during synchronous initialization',
            parameters: params,
            cause: error,
            stackTrace: stackTrace,
          );
        }
      } else {
        asyncInitInstances.add(instance);
      }
    }
  }

  // Phase 2: Asynchronous initialization
  if (asyncInitInstances.isNotEmpty) {
    final results = await Future.wait(
      asyncInitInstances.map((instance) async {
        final params = instances[instance]!;
        try {
          await instance._initSubsystem(params);
          return null;
        } catch (error, stackTrace) {
          return _InitError(instance: instance, params: params, error: error, stackTrace: stackTrace);
        }
      }),
    );

    final firstError = results.whereType<_InitError>().firstOrNull;
    if (firstError != null) {
      _failedRegistrations.add(firstError.params);
      await _cleanupFailedInstance(firstError.instance, firstError.params);
      if (atomicBatch) {
        await _rollbackBatch(instances);
      }
      throw SubsystemInitializationException(
        message: 'Failed during asynchronous initialization',
        parameters: firstError.params,
        cause: firstError.error,
        stackTrace: firstError.stackTrace,
      );
    }
  }

  // Phase 3: Post-initialization
  for (final entry in instances.entries) {
    final instance = entry.key;
    final params = entry.value;

    try {
      await instance._postInitSubsystem(params);
    } catch (error, stackTrace) {
      _failedRegistrations.add(params);
      await _cleanupFailedInstance(instance, params);
      if (atomicBatch) {
        await _rollbackBatch(instances);
      }
      throw SubsystemInitializationException(
        message: 'Failed during post-initialization',
        parameters: params,
        cause: error,
        stackTrace: stackTrace,
      );
    }
  }

  return instances.keys.toList();
}