# Workflows KT --- ## Why move? * Current tech stack (Workflow Foundation) no longer supported by Microsoft (since 2017) * Not .Net Core friendly * Technical debt making it impossible to make any changes (can't open diagrams in modern version of VS) * Current code ugly and messy (unless you like spaghetti) * Lack of testing/tests to capture states/state trnasitions --- ## MassTransit ![](https://i.imgur.com/KzG9vRH.png) --- ## Sagas * A saga is a long-lived transaction managed by a coordinator. Sagas are initiated by an event, sagas orchestrate events, and sagas maintain the state of the overall transaction. Sagas are designed to manage the complexity of a distributed transaction without locking and immediate consistency. They manage state and track any compensations required if a partial failure occurs. --- # Managing state A state machine defines the states, events, and behavior of a finite state machine. Implemented as a class, which is derived from MassTransitStateMachine<T/>, a state machine is created once, and then used to apply event triggered behavior to state machine instances. --- ## Main components * StateMachine (defines states, events and transition logic) * DealEvenState table (where states for workflows are stored) * Consumers (listen to events, run some code and respond back to state machine) * Activities (run some code as per definition in state machine) * Interfaces (definition of events) --- ## Flow * Deliver the message (event) to StateMachine * StateMachine will look up the workflow id from `DealEventState` table (CorrelationId) * StateMachine will move the state (or optionally publish a message ) as per the logic in the `DealEventStateMachine.cs` ![](https://i.imgur.com/dQVvSgu.png) --- ## Deal Event State Machine (Events and States) ```csharp= // States public State Created { get; private set; } public State AwaitingValidation { get; private set; } public State AwaitingAction { get; private set; } public State AwaitingCounterpartyConfirmation { get; private set; } public State MasterAgreementIncorrect { get; private set; } public State NoConfirmationRequired { get; private set; } //... and more //Events public Event<CreateWorkflowRequested> CreateRequested { get; private set; } public Event<WorkflowStatusRequested> StatusRequested { get; private set; } public Event<AwaitingValidationRequested> AwaitingValidationRequested { get; private set; } //... and more ``` --- ## Events as .NET interfaces ```csharp= namespace Claws.WorkFlow.MT.Contracts { public interface CreateWorkflowRequested { Guid WorkflowId { get; } int DealId { get; } int BusinessEventId { get; } int UserId { get; } string UserCode { get; set; } string Reference { get; set; } } } ``` --- ## State Transitions ```csharp= // AwaitingValidation During(AwaitingValidation, // state When(MasterAgreementStatus) // event .Then(x => { x.Instance.EConfirmationRequired = x.Data.EConfirmationRequired; }) .IfElse(x => x.Data.IsMasterAgreementValid, isMaValid => isMaValid .SetSystemUser() .SetMACorrectStatus() .PublishAsync(x => x.Init<CheckDealBrokeredStatus>(new { x.Data.WorkflowId, x.Data.DealId })), isMaInvalid => isMaInvalid .TransitionTo(MasterAgreementIncorrect) // new state .SetSystemUser() .SetMAIncorrectStatus() .RespondWorkflowProcessed()) ``` --- ## DealEventState table * Holds the state of current workflow ![](https://i.imgur.com/c3Ffsmx.png) --- ## Consumers ```csharp= public class BuySellStatusCheckerConsumer : IConsumer<CheckIsDealBuyStatus> { // ... code omitted for brevity public async Task Consume(ConsumeContext<CheckIsDealBuyStatus> context) { var workflowId = context.Message.WorkflowId; var dealId = context.Message.DealId; try { var isBuyDeal = _workflowValidations.IsDealBuy(dealId); _logger.LogDebug("Publishing [ IsDealBuy ] status [ {Status} ] for deal [{DealId}], workflow [{WorkflowId}]", isBuyDeal ? "Buy" : "Sell", dealId, workflowId); await context.RespondAsync<DealBuyStatus>(new { DealId = dealId, IsBuyDeal = isBuyDeal, WorkflowId = workflowId }); } catch (Exception e) { await context.RespondAsync<WorkflowStageFaulted>(new { StageName = nameof(BuySellStatusCheckerConsumer), DealId = dealId, Exception = e, WorkflowId = workflowId }); } } } ``` --- ## Activities ```csharp= public class SetBrokeredStatusActivity : ActivityBase<DealEventState> { public SetBrokeredStatusActivity(IDealManagementFactory dealManagementFactory, IWorkflowStageHelper workflowStageHelper) : base(dealManagementFactory, workflowStageHelper) { } protected override async Task Execute(BehaviorContext<DealEventState> context) { var state = context.Instance; state.Unmatched = true; state.NotBrokered = false; state.CurrentMatchingStatus = WorkflowStates.Unmatched; var eventMessage = $"DealID {state.DealId} Deal Reference '{state.SourceSystemName}' " + $"Matching Status moved to '{state.CurrentMatchingStatus}' by {state.UserCode}"; await PostProcess(state, eventMessage); } } ``` --- ```graphviz digraph G { 0 [shape=ellipse, label="Initial"]; 1 [shape=ellipse, label="Created"]; 2 [shape=ellipse, label="AwaitingValidation"]; 3 [shape=ellipse, label="DealDeleteReceived"]; 4 [shape=ellipse, label="DealEditReceived"]; 5 [shape=ellipse, label="Faulted"]; 6 [shape=ellipse, label="MasterAgreementIncorrect"]; 7 [shape=ellipse, label="NoConfirmationRequired"]; 8 [shape=ellipse, label="EConfirmationRequired"]; 9 [shape=ellipse, label="AwaitingCounterpartyConfirmation"]; 10 [shape=ellipse, label="RequiresConfirmation"]; 11 [shape=ellipse, label="AwaitingAction"]; 12 [shape=ellipse, label="CounterpartyContractReceived"]; 13 [shape=ellipse, label="BrokerDisputed"]; 14 [shape=ellipse, label="EndConfirmationState"]; 15 [shape=ellipse, label="EConfirmationInProgress"]; 16 [shape=ellipse, label="EConfirmationMatched"]; 17 [shape=ellipse, label="BPContractGenerated"]; 18 [shape=ellipse, label="BPContractSent"]; 19 [shape=ellipse, label="Disputed"]; 20 [shape=ellipse, label="BPContractConfirmed"]; 21 [shape=ellipse, label="BPContractDeemedConfirmed"]; 22 [shape=ellipse, label="BPContractResent"]; 23 [shape=ellipse, label="BPContractSentToTrader"]; 24 [shape=ellipse, label="CounterpartyContractSent"]; 25 [shape=rectangle, label="CreateRequested<CreateWorkflowRequested>"]; 26 [shape=rectangle, label="AwaitingValidationRequested<AwaitingValidationRequested>"]; 27 [shape=rectangle, label="DeleteRequestedEvent<DeleteRequested>"]; 28 [shape=rectangle, label="EditRequestedEvent<EditRequested>"]; 29 [shape=rectangle, label="WorkflowStageFaulted<WorkflowStageFaulted>"]; 30 [shape=rectangle, label="MasterAgreementStatus<MasterAgreementStatus>"]; 31 [shape=rectangle, label="DealBrokeredStatus<DealBrokeredStatus>"]; 32 [shape=rectangle, label="MasterAgreementRequiresConfirmationStatus<MasterAgreementRequiresConfirmationStatus>"]; 33 [shape=rectangle, label="EConfirmationStatusRequested<CheckEConfirmationStatus>"]; 34 [shape=rectangle, label="DealBuyStatus<DealBuyStatus>"]; 35 [shape=rectangle, label="AwaitingActionRequested<AwaitingActionRequested>"]; 36 [shape=rectangle, label="CounterpartyContractReceivedEvent<CounterpartyContractReceived>"]; 37 [shape=rectangle, label="RequiresConfirmationRequested<RequiresConfirmationRequested>"]; 38 [shape=rectangle, label="BrokerMatchedEvent<BrokerMatched>"]; 39 [shape=rectangle, label="BrokerMatchedStatus<IsBrokerMatchedStatus>"]; 40 [shape=rectangle, label="EConfirmationInProgressEvent<EConfirmationInProgress>"]; 41 [shape=rectangle, label="EConfirmationMatchedEvent<EConfirmationMatched>"]; 42 [shape=rectangle, label="BPContractGeneratedEvent<BPContractGenerated>"]; 43 [shape=rectangle, label="DisputedEvent<Disputed>"]; 44 [shape=rectangle, label="BPContractConfirmedEvent<BPContractConfirmed>"]; 45 [shape=rectangle, label="SetIsExceptionBPDeemedConfirmed<SetIsExceptionBPDeemedConfirmed>"]; 46 [shape=rectangle, label="ContractDeemedConfirmed<BPContractDeemedConfirmed>"]; 47 [shape=rectangle, label="BPContractResentEvent<BPContractResent>"]; 48 [shape=rectangle, label="BPContractSentEvent<BPContractSent>"]; 49 [shape=rectangle, label="BPContractSentToTraderEvent<BPContractSentToTrader>"]; 50 [shape=rectangle, label="CounterpartyContractSentEvent<CounterpartyContractSent>"]; 51 [shape=rectangle, label="DeleteAcceptedEvent<HeadingAcceptDeleteRequested>"]; 52 [shape=rectangle, label="EditAcceptedEvent<HeadingAcceptEditRequested>"]; 0 -> 25; 1 -> 25; 1 -> 26; 1 -> 27; 1 -> 28; 1 -> 29; 2 -> 30; 2 -> 31; 2 -> 32; 2 -> 33; 2 -> 34; 2 -> 35; 2 -> 26; 2 -> 27; 2 -> 28; 2 -> 29; 3 -> 27; 3 -> 51; 3 -> 28; 3 -> 29; 4 -> 27; 4 -> 28; 4 -> 52; 4 -> 29; 5 -> 25; 5 -> 26; 5 -> 27; 5 -> 28; 5 -> 29; 6 -> 35; 6 -> 26; 6 -> 27; 6 -> 28; 6 -> 29; 7 -> 35; 7 -> 26; 7 -> 27; 7 -> 28; 7 -> 39; 7 -> 38; 7 -> 29; 8 -> 40; 8 -> 38; 8 -> 35; 8 -> 26; 8 -> 27; 8 -> 28; 8 -> 29; 9 -> 36; 9 -> 37; 9 -> 38; 9 -> 31; 9 -> 35; 9 -> 26; 9 -> 27; 9 -> 28; 9 -> 29; 10 -> 35; 10 -> 26; 10 -> 27; 10 -> 28; 10 -> 42; 10 -> 38; 10 -> 31; 10 -> 29; 11 -> 35; 11 -> 26; 11 -> 27; 11 -> 28; 11 -> 29; 12 -> 35; 12 -> 26; 12 -> 43; 12 -> 50; 12 -> 38; 12 -> 31; 12 -> 27; 12 -> 28; 12 -> 29; 13 -> 35; 13 -> 26; 13 -> 27; 13 -> 28; 13 -> 29; 14 -> 27; 14 -> 28; 14 -> 26; 14 -> 29; 15 -> 41; 15 -> 38; 15 -> 35; 15 -> 26; 15 -> 27; 15 -> 28; 15 -> 29; 16 -> 31; 16 -> 39; 16 -> 38; 16 -> 35; 16 -> 26; 16 -> 27; 16 -> 28; 16 -> 29; 17 -> 35; 17 -> 26; 17 -> 48; 17 -> 37; 17 -> 38; 17 -> 31; 17 -> 49; 17 -> 27; 17 -> 28; 17 -> 29; 18 -> 35; 18 -> 26; 18 -> 43; 18 -> 44; 18 -> 45; 18 -> 46; 18 -> 38; 18 -> 31; 18 -> 47; 18 -> 37; 18 -> 27; 18 -> 28; 18 -> 29; 19 -> 35; 19 -> 26; 19 -> 27; 19 -> 28; 19 -> 50; 19 -> 44; 19 -> 29; 20 -> 35; 20 -> 26; 20 -> 38; 20 -> 31; 20 -> 39; 20 -> 27; 20 -> 28; 20 -> 29; 21 -> 35; 21 -> 26; 21 -> 43; 21 -> 44; 21 -> 38; 21 -> 31; 21 -> 27; 21 -> 28; 21 -> 29; 22 -> 35; 22 -> 26; 22 -> 43; 22 -> 44; 22 -> 45; 22 -> 46; 22 -> 38; 22 -> 31; 22 -> 27; 22 -> 28; 22 -> 29; 23 -> 35; 23 -> 26; 23 -> 43; 23 -> 44; 23 -> 45; 23 -> 46; 23 -> 38; 23 -> 31; 23 -> 42; 23 -> 27; 23 -> 28; 23 -> 29; 24 -> 35; 24 -> 26; 24 -> 39; 24 -> 38; 24 -> 31; 24 -> 27; 24 -> 28; 24 -> 29; 25 -> 1; 26 -> 2; 27 -> 3; 28 -> 4; 29 -> 5; 30 -> 6; 31 -> 13; 31 -> 14; 32 -> 7; 33 -> 8; 34 -> 9; 34 -> 10; 35 -> 11; 36 -> 12; 37 -> 10; 38 -> 14; 39 -> 14; 40 -> 15; 41 -> 16; 42 -> 17; 43 -> 19; 44 -> 20; 45 -> 21; 46 -> 21; 47 -> 22; 48 -> 18; 49 -> 23; 50 -> 24; 51 -> 14; 52 -> 2; } ``` ## Q's ?
{"metaMigratedAt":"2023-06-16T20:01:27.600Z","metaMigratedFrom":"Content","title":"Workflows KT","breaks":true,"contributors":"[{\"id\":\"c3d77e6f-86ed-47fc-8c64-010aba4c9dd1\",\"add\":14122,\"del\":2451}]"}
    239 views