This project is a take-home exercise to implement a minimal backend service for a configurable workflow engine based on state machines. The API allows clients to define workflows, start instances, and transition them between states by executing actions.
- Language/Framework: C# / .NET 8
- API Style: ASP.NET Core Minimal APIs
- Persistence: Simple in-memory dictionaries. No database is required.
- Dependencies: The project uses the default ASP.NET Core web SDK and Swashbuckle.AspNetCore for API documentation.
- .NET 8 SDK
-
Clone the repository:
git clone <your-repo-url> cd Infonetica.WorkflowEngine
-
Ensure Development Environment is Set: This project uses
Properties/launchSettings.jsonto automatically configure the application to run inDevelopmentmode. This ensures the Swagger API page is available. -
Run the application:
dotnet run
-
Access the API:
- The application will start and listen on
http://localhost:5000. - You can explore and interact with the API using the built-in Swagger UI at: http://localhost:5000/swagger
- The application will start and listen on
- Persistence: Data is stored in-memory using
ConcurrentDictionary. The data will be lost when the application restarts, as allowed by the requirements. - IDs:
WorkflowDefinitionandWorkflowInstanceIDs are generated as GUIDs for simplicity. State and Action IDs are expected to be meaningful strings provided by the client. - Validation: Validation is performed within the
WorkflowService. It rejects invalid definitions and invalid action executions. - Known Limitations: The assignment was time-boxed to approximately 2 hours. As such, the code includes
// TODOcomments for potential improvements that would be addressed with more time.
-
Create a Workflow Definition:
- Execute
POST /api/workflows/definitions - Provide the following JSON in the request body:
{ "id": "leave-request", "states": [ { "id": "draft", "name": "Draft", "isInitial": true }, { "id": "submitted", "name": "Submitted for Approval" }, { "id": "approved", "name": "Approved", "isFinal": true }, { "id": "rejected", "name": "Rejected", "isFinal": true } ], "actions": [ { "id": "submit", "name": "Submit", "fromStates": ["draft"], "toState": "submitted" }, { "id": "approve", "name": "Approve", "fromStates": ["submitted"], "toState": "approved" }, { "id": "reject", "name": "Reject", "fromStates": ["submitted"], "toState": "rejected" } ] }
- Execute
-
Start a Workflow Instance:
- Execute
POST /api/workflows/instances - Body:
{"definitionId": "leave-request"} - This returns a new instance in the
draftstate. Note theidfrom the response.
- Execute
-
Execute an Action:
- Execute
POST /api/workflows/instances/{instanceId}/actions/{actionId} - Use the instance
idfrom the previous step for{instanceId}and"submit"for{actionId}. - This moves the instance to the
submittedstate, and its history is updated.
- Execute