Code generator
go-bpm-gen validates BPMN and DMN files, produces typed Go code and compiles the generated packages. Handwritten methods and handlers remain part of your service.
Install and run#
go get -tool github.com/nicolas-vp/go-bpm/cmd/go-bpm-gen@latest
go tool go-bpm-genUse the same engine and generator version. Default paths are relative to the module's go.mod: input is process/, output is internal/bpmgen/. Run inside a Go module, or specify both input and output paths with the generator flags.
Add a generation directive to a package in your service:
package gen
//go:generate go tool go-bpm-genThen run:
go generate ./...Generated layout#
process/bpmn-gen.go embedded BPMN and DMN
internal/bpmgen/
bpm-gen.go process registry
queue-gen.go handler queues
model/data_model.go your typed variables
services/services.go your dependencies
handlers/notify_client.go your service task handler
order_approval/
flow-gen.go generated process graph
method_check_order.go your synchronous method
condition_*.go generated conditions
decision_*.go generated DMN functionsFiles marked Code generated by go-bpm-gen. DO NOT EDIT. are regenerated. Model, service, method and handler stubs are created once and then preserved. Keep handwritten logic in those files.
Data model#
type DataModel struct {
Amount float64 `json:"amount"`
Checked bool `json:"checked"`
Notified bool `json:"notified"`
}The JSON tag defines the process variable name. One model is shared across processes. Nested structs support nested variable paths. Missing fields stop generation with a suggested field declaration; review the inferred type before adding it.
Methods see typed fields. After a method runs, fields merge back into variables. Unknown variables survive, and omitempty controls whether zero-valued fields are omitted from the merge.
Synchronous methods#
An ordinary task calls a method within the current process step:
func CheckOrder(ctx context.Context, svc *services.Services, data *model.DataModel) error {
if data.Amount <= 0 {
return fmt.Errorf("order amount must be positive: %v", data.Amount)
}
data.Checked = true
return nil
}A later error in the step rolls back its database effects and causes the whole step to repeat. Keep irreversible external operations in idempotent service task handlers.
Service task handlers#
A service task uses a jobType, such as notifyClient. The generator creates its queue registration and handler stub:
func NotifyClient(ctx context.Context, svc *services.Services, job bpm.Job, data *model.DataModel) error {
slog.InfoContext(ctx, "notifying customer", "instance", job.InstanceUid)
data.Notified = true
return nil
}The handler runs in a separate queue task with BPMN retry settings. Put external clients in services.Services; use interfaces to substitute test doubles.
Conditions, scripts and decisions#
Gateway expressions and scripts become Go functions over DataModel. DMN tables become functions in each calling process package, with typed results and matched rule IDs. Compilation catches missing fields and invalid operations. See BPMN and DMN.
Changing a diagram#
| Change | Generator behavior |
|---|---|
| Add a task | Create a method or handler stub |
| Remove a task | Remove it from the graph; retain handwritten files |
| Rename an element ID | Create a new stub; move your code manually |
| Remove a job type | Retain its handwritten handler |
| Remove a process | Remove generated files; retain handwritten files with a warning |
| Edit a condition, script or decision table | Regenerate the implementation |
Delete obsolete handwritten stubs when they are no longer used.
Running instances and process versions#
Tokens store an element ID and variables. Removing or renaming an element with waiting tokens can break existing instances after deployment. Retain it until those instances have passed, or run old and new service versions with distinct queue KindPrefix settings. See operations.
Troubleshooting#
- Missing variables: add the suggested model fields and regenerate.
- Invalid expression: fix its Go-compatible syntax and types.
- Unsupported BPMN: replace it with a supported element.
- Several outgoing task flows: insert a gateway.
- Missing service job type, DMN reference or result variable: complete its properties.
- Duplicate generated names: choose distinct process and element IDs.
- Invalid timer: use an ISO 8601 duration without years/months or a zoned RFC 3339 date.
- Broken XML connections: correct
sourceRef/targetRefand any explicit incoming/outgoing declarations.
CI check#
go generate ./...
git diff --exit-codeThis catches diagrams committed without regenerated code and accidental edits to generated files.