Workflow
A flexible workflow engine for orchestrating steps, tasks, and data in Go applications. Built on Petri Net foundations for reliable flow management, with a focus on portability and visualization. Inspired by Symfony Workflow Component.
Install / Use
/learn @ehabterra/WorkflowREADME
Go Workflow
⚠️ A Note from the Developers:
This workflow engine is currently in active development. We're building it to be reliable and easy to use, but it's not quite ready for mission-critical production yet. We're a growing open-source project, and we truly welcome any feedback, contributions, or real-world testing you can offer to help us make it great.If you like the idea of building smart, durable workflows in Go, please join us!
What is Go Workflow?
Go Workflow is a flexible engine for orchestrating steps, tasks, and data within your Go applications.
Our engine is rooted in Petri Net theory, which is just a fancy way of saying we use a rock-solid mathematical foundation to manage flow. This gives you a huge advantage: when you model a complex process with many parallel paths, you get built-in assurance that your workflow won't end up in a confusing or deadlocked state.
We're focused on portability and visualization, allowing you to change your complex processes easily without touching or recompiling your application code.
Inspired by Symfony Workflow Component.
Key Features
💻 Keep Your Code Clean & Flexible (Portability)
- YAML Processes: Define your entire process structure and logic in simple YAML configuration files. This means your business teams can look at the flow, and your engineering team can deploy updates instantly.
- Dynamic Decision Logic: Use an expressive guard language (powered by expr-lang/expr) to handle decisions like "If the amount is over $1000, send to manager approval". This logic lives in your config file, keeping it flexible and separate from your Go application code.
- Context-Aware Storage: We built a flexible layer to reliably save the flow's current state and all its necessary data (like an
order_idoruser_role) to a database, ensuring long-running processes are safe from crashes.
✅ Built for Reliability (Petri Net Power)
- Mathematically Sound: Our Petri Net core helps ensure that even processes with lots of parallel paths and complex merging logic are deadlock-free and always reach a correct conclusion.
- Thread-Safe Registry: The workflow registry uses proper locking to safely handle concurrent access from multiple goroutines.
- Audit Trail Ready: Our pluggable history layer automatically logs every transition, allowing you to track exactly who did what and when.
📊 Easy to Understand (Visualization)
- Mermaid Diagram Generation: Quickly generate visual flowcharts from your definition. You can paste these diagrams into your documentation (like this README!) for instant visualization.
- Workflow Manager: A clean, straightforward interface for loading, starting, and saving all your running workflow instances.
Technical Highlights
- Support for multiple states and parallel transitions
- Event system for workflow lifecycle hooks
- Constraint system for custom validation logic
- Comprehensive test coverage
Storage Layer
We designed the storage interface to be flexible. You tell us where to put the data and what extra information you need to store.
Storage Interface
The package provides a flexible, context-aware storage interface for persisting workflow states and custom fields. Only fields declared as custom fields are persisted:
type Storage interface {
LoadState(id string) (places []Place, context map[string]interface{}, err error)
SaveState(id string, places []Place, context map[string]interface{}) error
DeleteState(id string) error
}
You can implement your own storage backend by implementing this interface. The package includes a SQLite implementation with options for custom fields:
import "github.com/ehabterra/workflow/storage"
// Create a SQLite storage with custom fields
storage, err := storage.NewSQLiteStorage(db,
storage.WithTable("workflow_states"),
storage.WithCustomFields(map[string]string{
"title": "title TEXT",
"owner": "owner TEXT",
}),
)
if err != nil { panic(err) }
// Generate and initialize schema
schema := storage.GenerateSchema()
if err := storage.Initialize(db, schema); err != nil { panic(err) }
// Save state with context
err = storage.SaveState("my-workflow", []workflow.Place{"draft"}, map[string]interface{}{"title": "My Doc", "owner": "alice"})
// Load state and context
places, ctx, err := storage.LoadState("my-workflow")
fmt.Println(places, ctx["title"], ctx["owner"])
History Layer
The history layer is a pluggable audit trail, letting you track and query every single state change that happens in your processes. It supports custom fields, pagination, and filtering.
History Interface
type HistoryStore interface {
SaveTransition(record *TransitionRecord) error
ListHistory(workflowID string, opts QueryOptions) ([]TransitionRecord, error)
GenerateSchema() string
Initialize() error
}
SQLite History Example
import "github.com/ehabterra/workflow/history"
historyStore := history.NewSQLiteHistory(db,
history.WithCustomFields(map[string]string{
"ip_address": "ip_address TEXT",
}),
)
historyStore.Initialize()
// Save a transition with custom fields
historyStore.SaveTransition(&history.TransitionRecord{
WorkflowID: "wf1",
FromState: "draft",
ToState: "review",
Transition: "submit",
Notes: "Submitted for review",
Actor: "alice",
CreatedAt: time.Now(),
CustomFields: map[string]interface{}{
"ip_address": "127.0.0.1",
},
})
// List history with pagination
records, err := historyStore.ListHistory("wf1", history.QueryOptions{Limit: 10, Offset: 0})
for _, rec := range records {
fmt.Println(rec.FromState, rec.ToState, rec.Notes, rec.CustomFields["ip_address"])
}
Feature Checklist
Current Features ✅
- [x] Basic workflow definition and execution
- [x] Multiple states and transitions
- [x] Event system for workflow hooks
- [x] Constraint system for transitions
- [x] Thread-safe workflow registry
- [x] Mermaid diagram visualization
- [x] Workflow manager for lifecycle management
- [x] Storage interface for persistence
- [x] SQLite storage implementation
- [x] Support for parallel transitions and branching
- [x] Workflow history and audit trail (in examples)
- [x] Web UI for workflow management (in examples)
- [x] YAML configuration support
Note: The provided example includes a Web UI for workflow management, but does not expose a REST API. If you need a REST API, contributions or feature requests are welcome!
Building Smarter Workflows: The Roadmap 🚀
We're starting with the basics of Petri Nets and adding layers of powerful features inspired by industry tools, focusing on solving common developer problems.
High Priority: Making it Durable and Multi-Tasking
| Feature Description | The Problem We're Solving | Petri Net Concept | | :--- | :--- | :--- | | Smart Tokens (Colored Petri Nets - CPN) | How do you process a batch of 100 orders within one workflow instance? | Tokens carry data. We'll let tokens carry attributes (like an order ID), allowing one process to manage multiple concurrent items. | | Nested Workflows (HCPN) | My process has 100 steps—it's too big to manage. | Modularity. You can define a complex sub-flow (like "Payment Verification") once and drop it into any main process as a single, clean step. | | Crash-Safe Storage (ACID) | What if the server dies during a critical step? | Data Integrity. We're adding transactional guarantees to ensure state changes are atomic (all or nothing) and durable. | | Undo Button (Compensation/Rollback) | I need a way to reliably "undo" a previous action if a later step fails (e.g., refunding a charge). | Error Recovery. We'll track the history of completed tasks precisely, enabling safe, structured rollbacks. | | Advanced Synchronization | When two parallel paths finish, how do I make the third step wait only for the first path, but cancel the second one? | Complex Merging. We're implementing advanced logic (like nested AND/OR/XOR conditions and discriminators) for robust handling of parallel flows. |
Medium Priority: Handling Time and External Events
| Feature Description | The Problem We're Solving | Petri Net Concept | | :--- | :--- | :--- | | Timeouts and Scheduled Steps (TPN/DPN) | I need a task to wait exactly 30 minutes before starting, or to timeout after 24 hours. | Time Awareness. Adding support for time constraints and scheduling to transitions. | | Workflow Checker (Validation System) | How do I know my new YAML definition won't cause a bug? | Pre-Execution Guarantees. We'll use Petri Net math to check the flow for common issues like deadlocks before you deploy it. | | Talk to Other Workflows (Message Correlation) | I need Workflow A to wait for a signal from a completely separate Workflow B. | Inter-Process Communication. Building a reliable way for instances to communicate and find each other using shared data keys. |
Additional Planned Features
- [ ] Build standalone web interface for workflow management
- [ ] Enhance REST API endpoints
- [ ] Add process variable scope m
