-
Notifications
You must be signed in to change notification settings - Fork 6
Description
Sometimes, I want to pass context on a success or a failure. For example, I would like to use this context when the saga ends.
For example, I am in a SubSaga and failed a validation. I would like to report on the main saga of validation failure. Having the context in the OnFailure callback provides the means to do it without introducing a state variable.
We can also pass on the operation that caused the Saga to fail to OnFailed.
https://chatgpt.com/share/671fa101-9dc4-8011-9d20-7cfd03995a95
Design Document: Adding Saga Summary with Per-Operation Context
- Motivation
In the current Saga pattern, the final outcome is provided through a log and a basic success/failure callback. To improve observability and debugging, developers need additional details at the end of the Saga execution, such as:
-
A comprehensive Saga summary with a detailed status of each operation.
-
A user-defined context that captures specific information per operation (outcome context) for further insights.
To achieve this, we propose adding a Saga Summary that includes:
-
A high-level summary for the entire Saga, including its execution log.
-
Details of each operation’s execution status.
-
A per-operation outcome context for customized information on each operation’s outcome.
The design aims to maintain backward compatibility while enhancing flexibility and observability.
- Design Overview
Saga Summary:
A new SagaSummary<TOutcomeContext> class that captures:
OperationDetails: A list of SagaOperationStatus, one for each operation.
Log: A full execution log for the Saga.
Operation-Level Outcome Context:
SagaOperationStatus will include:
Execution details (whether the operation was run, succeeded, failed, or reverted).
An OutcomeContext property to store user-defined context specific to each operation’s outcome.
Separate Context in Callbacks:
Modify the success and failure callbacks to accept both SagaSummary and a generic overall TContext as separate parameters, making the overall context more accessible and explicit.
- Required Changes
3.1 Define SagaOperationStatus to Include Outcome Context
Define a SagaOperationStatus class to track each operation’s execution status and any specific context associated with its outcome.
public class SagaOperationStatus<TOutcomeContext>
{
public string OperationName { get; set; }
public bool WasRun { get; set; }
public bool Succeeded { get; set; }
public bool Failed { get; set; }
public bool Reverted { get; set; }
/// <summary>
/// Context specific to the outcome of this operation
/// </summary>
public TOutcomeContext OutcomeContext { get; set; }
}
3.2 Define SagaSummary
The SagaSummary class will include OperationDetails, a list of each operation’s status and outcome context, and the full Saga log.
csharp
Copy code
public class SagaSummary
{
///
/// List of each operation's status and outcome context in the Saga
///
public List<SagaOperationStatus> OperationDetails { get; set; } = new List<SagaOperationStatus>();
/// <summary>
/// Complete log of the Saga execution
/// </summary>
public string Log { get; set; }
}
3.3 Update Saga to Track Operation-Level Contexts and Generate Summary
Modify the Saga class to:
Track each operation’s status and its outcome context.
Pass both the SagaSummary and the user-defined overall context to the completion callbacks.
csharp
Copy code
public class Saga
{
private List<SagaOperationStatus> _operationStatuses = new List<SagaOperationStatus>();
// Reporting operation outcome with individual outcome context
public async Task ReportOperationOutcomeAsync<TOutcomeContext>(TEOperations operation, bool isSuccessful,
SagaFastOutcome sagaFastOutcome, TOutcomeContext outcomeContext)
{
var operationStatus = _operationStatuses.FirstOrDefault(op => op.OperationName == operation.ToString())
?? new SagaOperationStatus<object> { OperationName = operation.ToString() };
operationStatus.WasRun = true;
operationStatus.Succeeded = isSuccessful;
operationStatus.Failed = !isSuccessful;
operationStatus.OutcomeContext = outcomeContext;
_operationStatuses.Add(operationStatus);
}
protected virtual async Task OnSucceeded<TContext>(TContext overallContext)
{
var summary = new SagaSummary<object>
{
OperationDetails = _operationStatuses,
Log = GenerateLog()
};
_onSuccessCompletionCallback?.Invoke(summary, overallContext);
}
protected virtual async Task OnFailed<TContext>(TContext overallContext)
{
var summary = new SagaSummary<object>
{
OperationDetails = _operationStatuses,
Log = GenerateLog()
};
_onFailedCallback?.Invoke(summary, overallContext);
}
}
3.4 Update SagaBuilder to Define Callbacks with Separate Context Parameters
The SagaBuilder now supports callbacks that accept both SagaSummary and a separate overall TContext.
csharp
Copy code
public class SagaBuilder
{
public SagaBuilder WithOnSuccessCompletionCallback<TContext, TOutcomeContext>(Action<SagaSummary, TContext> onSuccessCompletionCallback)
{
_onSuccessCompletionCallback = onSuccessCompletionCallback;
return this;
}
public SagaBuilder WithOnFailedCallback<TContext, TOutcomeContext>(Action<SagaSummary<TOutcomeContext>, TContext> onFailedCallback)
{
_onFailedCallback = onFailedCallback;
return this;
}
}
- GitHub Project Development Tasks
Add SagaOperationStatus Class
Define SagaOperationStatus to hold operation status and outcome context properties.
Add properties for OperationName, WasRun, Succeeded, Failed, Reverted, and OutcomeContext.
Add SagaSummary Class
Define SagaSummary to encapsulate the Saga execution log and operation statuses.
Include OperationDetails as a list of SagaOperationStatus.
Include Log property for the full Saga execution log.
Update Saga Class to Use SagaSummary and Operation Contexts
Track operation status and context in ReportOperationOutcomeAsync using SagaOperationStatus.
Generate a SagaSummary in OnSucceeded and OnFailed.
Pass SagaSummary and the separate overall context to completion callbacks.
Update SagaBuilder for Separate Context Parameters in Callbacks
Modify WithOnSuccessCompletionCallback to accept both SagaSummary and TContext.
Modify WithOnFailedCallback similarly to accept SagaSummary and TContext.
Testing and Validation
Write unit tests to confirm operation status and outcome context are correctly recorded.
Add tests for SagaSummary structure to validate OperationDetails and Log.
Test SagaBuilder callbacks to ensure proper handling of SagaSummary and overall context.
Documentation Update
Update the method summaries and usage examples to reflect the new SagaSummary and per-operation contexts.
Provide example usage of WithOnSuccessCompletionCallback and WithOnFailedCallback with both SagaSummary and context.
Code Review and Merge
Conduct a code review to ensure backward compatibility and consistency.
Merge changes to the main branch after tests and reviews are completed.
