Skip to main content

Sharing data across modules

Modules have been designed with data and sharing at its core.

When a module returns data in its ExecuteAsync method, that data is available to be seen by other modules.

Call await context.GetModule<TModule>() from within your module to access another module's result.

[DependsOn<BuildModule>]
public class DeployModule : Module<DeployResult>
{
protected override async Task<DeployResult?> ExecuteAsync(
IModuleContext context, CancellationToken cancellationToken)
{
// Get the build module's result
var buildResult = await context.GetModule<BuildModule>();

// Access the value safely
var artifact = buildResult.ValueOrDefault?.ArtifactPath;

return await Deploy(artifact);
}
}

Handling Different Outcomes

Module results are a discriminated union with three possible states: Success, Failure, or Skipped. Use pattern matching to handle each case:

var result = await context.GetModule<MyModule>();

// Pattern matching (recommended)
return result switch
{
ModuleResult<MyResult>.Success { Value: var value }
=> await ProcessValue(value),
ModuleResult.Failure { Exception: var ex }
=> HandleFailure(ex),
ModuleResult.Skipped { Decision: var skip }
=> HandleSkipped(skip.Reason),
_ => null
};

Using Match Helper

For exhaustive handling, use the Match method:

var result = await context.GetModule<MyModule>();

return result.Match(
onSuccess: value => Process(value),
onFailure: ex => HandleError(ex),
onSkipped: skip => HandleSkip(skip)
);

Convenience Properties

For simpler checks, use the convenience properties:

var result = await context.GetModule<MyModule>();

// Quick status checks
if (result.IsSuccess)
{
var value = result.ValueOrDefault;
// Process value
}

if (result.IsFailure)
{
var exception = result.ExceptionOrDefault;
// Handle error
}

if (result.IsSkipped)
{
var skipDecision = result.SkipDecisionOrDefault;
// Handle skip
}

Important: Declare Dependencies

Always declare dependencies using [DependsOn<T>] to ensure the dependent module has completed before you call GetModule:

[DependsOn<BuildModule>]  // Ensures BuildModule completes first
[DependsOn<TestModule>] // Ensures TestModule completes first
public class DeployModule : Module<DeployResult>
{
protected override async Task<DeployResult?> ExecuteAsync(
IModuleContext context, CancellationToken cancellationToken)
{
// Safe to call - dependencies are guaranteed to be complete
var build = await context.GetModule<BuildModule>();
var tests = await context.GetModule<TestModule>();

// ...
}
}