ConvergeERP.Shared.Workflow
0.0.4-beta
dotnet add package ConvergeERP.Shared.Workflow --version 0.0.4-beta
NuGet\Install-Package ConvergeERP.Shared.Workflow -Version 0.0.4-beta
<PackageReference Include="ConvergeERP.Shared.Workflow" Version="0.0.4-beta" />
<PackageVersion Include="ConvergeERP.Shared.Workflow" Version="0.0.4-beta" />
<PackageReference Include="ConvergeERP.Shared.Workflow" />
paket add ConvergeERP.Shared.Workflow --version 0.0.4-beta
#r "nuget: ConvergeERP.Shared.Workflow, 0.0.4-beta"
#:package ConvergeERP.Shared.Workflow@0.0.4-beta
#addin nuget:?package=ConvergeERP.Shared.Workflow&version=0.0.4-beta&prerelease
#tool nuget:?package=ConvergeERP.Shared.Workflow&version=0.0.4-beta&prerelease
ConvergeERP.Shared.Workflow
Shared workflow implementation, HTTP client SDK, and resilience patterns for ConvergeERP services.
Version 1.0.0
Target Framework: .NET 10
License: MIT
Table of Contents
- Overview
- Package Split
- Installation
- Quick Start
- Registration Methods
- Architecture
- Configuration
- Usage Examples
- Health Checks
- Troubleshooting
- API Reference
Overview
This library provides:
| Feature | Description |
|---|---|
| HTTP Client SDK | Call workflow API endpoints with resilience |
| Event Publishing | Publish workflow events via RabbitMQ/Kafka (optional) |
| Resilience | Retry policies, circuit breaker, correlation propagation |
| Caching | Optional definition caching to reduce API calls |
| Health Checks | Monitor workflow service connectivity |
| Posting Gateway | Check if document posting is allowed |
Package Split
| Package | Purpose | Message Bus Required? |
|---|---|---|
| ConvergeERP.Shared.Workflow.Core | Contracts, interfaces, DTOs (lightweight) | No |
| ConvergeERP.Shared.Workflow | Full implementation (this package) | Only for events |
Installation
<PackageReference Include="ConvergeERP.Shared.Workflow" Version="1.0.0" />
Quick Start
⭐ Option 1: HTTP Only (Most Common - No Message Bus Required)
Use this if you only need to call workflow API endpoints:
// appsettings.json
{
"WorkflowClient": {
"BaseUrl": "https://workflow-service.example.com"
}
}
// Program.cs
services.AddConvergeWorkflowSdk(configuration);
// Usage in your service
public class InvoiceService
{
private readonly IWorkflowRuntimeClient _workflowClient;
private readonly IPostingGateway _postingGateway;
public async Task<bool> CanPostInvoice(Guid invoiceId)
{
var result = await _postingGateway.IsPostingAllowedAsync("Invoice", invoiceId);
return result.IsAllowed;
}
public async Task SubmitForApproval(Guid invoiceId)
{
await _workflowClient.CreateInstanceAsync(new CreateInstanceRequest
{
DocumentType = "Invoice",
DocumentId = invoiceId
});
}
}
Option 2: With Event Publishing (Requires RabbitMQ)
Use this if you need to publish/subscribe to workflow events:
// appsettings.json
{
"WorkflowClient": {
"BaseUrl": "https://workflow-service.example.com"
},
"RabbitMQ": {
"Host": "rabbitmq://localhost",
"Username": "guest",
"Password": "guest"
}
}
// Program.cs - ORDER MATTERS!
services.AddRabbitMQMessagePublisher(configuration); // ⚠️ MUST come first!
services.AddConvergeWorkflowFull(configuration); // Then this
Option 3: Safe Mode (Events Optional)
Use this if events are nice-to-have but not required:
// Program.cs
services.AddConvergeWorkflowSafe(configuration);
// Events will be no-op if RabbitMQ is not configured
Registration Methods
| Method | Description | Requires RabbitMQ? |
|---|---|---|
AddConvergeWorkflowSdk(config) |
HTTP clients + PostingGateway | ❌ No |
AddConvergeWorkflowEvents() |
Event publisher/subscriber | ✅ Yes |
AddConvergeWorkflowFull(config) |
SDK + Events | ✅ Yes |
AddConvergeWorkflowSafe(config) |
SDK + Optional Events | ❌ No (graceful degradation) |
TryAddConvergeWorkflowEvents() |
Events if available, no-op otherwise | ❌ No |
Architecture
┌─────────────────────────────────────────────────────────────────────────┐
│ DOMAIN SERVICE │
│ (Finance, Procurement, HR, etc.) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────────┐ │
│ │ IPostingGateway │ │ IWorkflowRuntime │ │ IWorkflowEvent │ │
│ │ (Critical!) │ │ Client │ │ Publisher │ │
│ └────────┬─────────┘ └────────┬─────────┘ └─────────┬─────────┘ │
│ │ │ │ │
│ │ │ (Optional - needs │
│ │ │ RabbitMQ) │
│ ┌────────┴─────────────────────┴───────────────────────┴────────────┐ │
│ │ HTTP HANDLER PIPELINE │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Retry/ │ │Idempotenc│ │Correlatio│ │ Tenant │ │ Auth │ │ │
│ │ │ Circuit │→│ y Handler│→│ n Handler│→│ Context │→│ Handler │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────┐
│ WORKFLOW RUNTIME SERVICE │
└───────────────────────────────┘
Configuration
Minimal Configuration (HTTP Only)
{
"WorkflowClient": {
"BaseUrl": "https://workflow-service.example.com"
}
}
Full Configuration
{
"WorkflowClient": {
"BaseUrl": "https://workflow-service.example.com",
"TimeoutSeconds": 30,
"MaxRetryAttempts": 3,
"RetryBaseDelayMs": 500,
"RetryMaxDelayMs": 30000,
"EnableCircuitBreaker": true,
"CircuitBreakerFailureThreshold": 5,
"CircuitBreakerDurationSeconds": 30,
"EnableDefinitionCaching": true,
"DefinitionCacheMinutes": 15
},
"RabbitMQ": {
"Host": "rabbitmq://localhost",
"Username": "guest",
"Password": "guest"
}
}
Usage Examples
Check if Posting is Allowed
public class InvoicePostingService
{
private readonly IPostingGateway _postingGateway;
public async Task<IActionResult> Post(Guid invoiceId)
{
var check = await _postingGateway.IsPostingAllowedAsync("Invoice", invoiceId);
if (!check.IsAllowed)
{
return BadRequest($"Cannot post: {check.Reason}");
}
// Proceed with posting...
return Ok();
}
}
Create Workflow Instance
var result = await _workflowClient.CreateInstanceAsync(new CreateInstanceRequest
{
DocumentType = "Invoice",
DocumentId = invoiceId,
DocumentRef = "INV-2024-001",
Priority = 1,
Metadata = new Dictionary<string, string>
{
{ "Amount", "5000.00" },
{ "Vendor", "Acme Corp" }
}
});
if (result.Success)
{
Console.WriteLine($"Instance created: {result.InstanceId}");
}
Approve/Reject Workflow
// Approve
var approveResult = await _workflowClient.ApproveAsync(instanceId, new WorkflowActionRequest
{
Comment = "Looks good, approved."
});
// Reject
var rejectResult = await _workflowClient.RejectAsync(instanceId, new WorkflowActionRequest
{
Comment = "Amount exceeds budget limit."
});
Get My Approvals (Inbox)
var inbox = await _workflowClient.GetMyApprovalsAsync(new InboxQuery
{
DocumentType = "Invoice",
Status = WorkflowInstanceStatus.Pending,
Page = 1,
PageSize = 20
});
foreach (var item in inbox.Items)
{
Console.WriteLine($"{item.DocumentRef} - {item.CurrentStepName}");
}
Health Checks
// Program.cs
services.AddHealthChecks()
.AddWorkflowServiceHealthCheck();
// Endpoint
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
Troubleshooting
Error: "IMessagePublisher is not registered"
Cause: You called AddConvergeWorkflowEvents() or AddConvergeWorkflowFull() without configuring RabbitMQ first.
Solution A - If you need events:
// Add RabbitMQ BEFORE workflow events
services.AddRabbitMQMessagePublisher(configuration); // First!
services.AddConvergeWorkflowFull(configuration); // Second!
Solution B - If you don't need events:
// Use SDK only (no RabbitMQ required)
services.AddConvergeWorkflowSdk(configuration);
Solution C - If events are optional:
// Safe mode - events are no-op if RabbitMQ not configured
services.AddConvergeWorkflowSafe(configuration);
Error: "WorkflowClient:BaseUrl is required"
Cause: Missing configuration in appsettings.json.
Solution:
{
"WorkflowClient": {
"BaseUrl": "https://your-workflow-service.com"
}
}
API Reference
IWorkflowRuntimeClient
// Instance Management
Task<CreateInstanceResponse> CreateInstanceAsync(CreateInstanceRequest request, CancellationToken ct);
Task<WorkflowInstanceDto?> GetInstanceAsync(Guid instanceId, CancellationToken ct);
Task<WorkflowInstanceDto?> GetInstanceByDocumentAsync(string documentType, Guid documentId, CancellationToken ct);
// Actions
Task<WorkflowActionResponse> SubmitAsync(Guid instanceId, WorkflowActionRequest request, CancellationToken ct);
Task<WorkflowActionResponse> ApproveAsync(Guid instanceId, WorkflowActionRequest request, CancellationToken ct);
Task<WorkflowActionResponse> RejectAsync(Guid instanceId, WorkflowActionRequest request, CancellationToken ct);
Task<WorkflowActionResponse> CancelAsync(Guid instanceId, WorkflowActionRequest request, CancellationToken ct);
// Inbox
Task<InboxResponse> GetMyApprovalsAsync(InboxQuery? query, CancellationToken ct);
IPostingGateway
Task<PostingCheckResult> IsPostingAllowedAsync(
string documentType,
Guid documentId,
Guid? workflowInstanceId = null,
CancellationToken ct = default);
IWorkflowEventPublisher (Requires RabbitMQ)
Task PublishWorkflowInitializedAsync(WorkflowInitializedEvent wfEvent, CancellationToken token);
Task PublishInstanceCreatedAsync(WorkflowInstanceCreatedEvent instanceEvent, CancellationToken token);
Task PublishWorkflowCompletedAsync(WorkflowCompletedEvent completedEvent, CancellationToken token);
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- ConvergeERP.Shared.EventBus (>= 0.0.1-beta)
- ConvergeERP.Shared.Workflow.Core (>= 0.0.3-preview)
- Microsoft.Extensions.Http.Polly (>= 10.0.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.0.4-beta | 256 | 6/11/2026 |
| 0.0.2-preview | 345 | 4/2/2026 |
| 0.0.1-preview | 174 | 3/22/2026 |