Terminate end event¶
A plain end event consumes the one token that reaches it and lets the other
branches run on. A terminate end event carries a terminate trigger: the
instant one token arrives, the engine tears down the whole enclosing scope —
every other in-flight branch is cancelled and the instance settles in
Terminated instead of Completed. Reach for it when one branch decides there
is no point continuing: a fraud hit, a hard business rule, an unrecoverable
input. It is an ordinary EndEvent plus a TerminateEventDefinition trigger —
this page is the developer reference for building and running one.
Taxonomy¶
| BPMN category | Event → End Event → Terminate trigger (§13.5.6 / TerminateEventDefinition) |
| Package | github.com/dr-dobermann/gobpm/pkg/model/events |
| Type | events.EndEvent carrying an events.TerminateEventDefinition |
| Trigger | flow.TriggerTerminate ("Terminate") — returned by TerminateEventDefinition.Type() |
| Inherits | the end-event attributes (BaseElement id/documentation/extensions) |
| The work | collapse the enclosing scope — cancel sibling tracks, settle the instance Terminated |
Where it sits in the event family: Events taxonomy.
Constructor¶
There is no dedicated NewTerminateEndEvent. You build a normal end event and
attach a terminate trigger. Two calls:
func NewTerminateEventDefinition(
baseOpts ...options.Option,
) (*TerminateEventDefinition, error)
func NewEndEvent(
name string,
endEventOptions ...options.Option,
) (*EndEvent, error)
| Parameter | Meaning |
|---|---|
baseOpts |
zero or more base-element options for the definition (foundation.WithID, foundation.WithDoc). |
name |
the end event's diagram name (and default id source). |
endEventOptions |
end-event options — pass WithTerminateTrigger(ted) to make it a terminate. |
Both return an error, never panic, on an invalid argument or option combination.
Options¶
The terminate is one option on an otherwise plain end event:
| Option | When you reach for it |
|---|---|
WithTerminateTrigger(ted) |
turn the end event into a terminate — the only thing that distinguishes it from a plain end event. |
func WithTerminateTrigger(
ted *TerminateEventDefinition,
) options.Option
WithTerminateTrigger adds the TerminateEventDefinition into the end event's
config. Drop it and you are left with a plain end event that only consumes its
own token. An end event accepts the other end-event trigger options too —
WithErrorTrigger, WithEscalationTrigger, WithCancelTrigger,
WithCompensationTrigger, WithSignalTrigger, WithMessageTrigger (see
Start & End); terminate is the one that collapses the scope.
The trigger definition needs no arguments of its own —
NewTerminateEventDefinition()with no base options is the usual call. Its behavior is entirely in what the engine does when the token arrives.
For the complete, always-current signatures run
go doc github.com/dr-dobermann/gobpm/pkg/model/events.
Build it¶
Build the trigger, then attach it to an end event with WithTerminateTrigger.
The rest of the process is ordinary — a diverging parallel gateway splits into
two branches; the fraud branch ends at the terminate event, the payment branch
at a normal end event (from examples/terminate-end-event/process.go):
termEd, err := events.NewTerminateEventDefinition()
// ...
terminate, err := events.NewEndEvent("terminate-order",
events.WithTerminateTrigger(termEd))
// ...
paymentDone, err := events.NewEndEvent("payment-done") // a plain end event
split, _ := gateways.NewParallelGateway(gateways.WithDirection(gateways.Diverging))
flow.Link(start, split)
flow.Link(split, fraudCheck)
flow.Link(fraudCheck, terminate) // this branch collapses the instance
flow.Link(split, payment)
flow.Link(payment, paymentDone) // this branch is cancelled mid-charge
The payment operation is long-running and honors its context — that is what
lets the engine cancel it when the terminate fires (from handlers.go):
select {
case <-time.After(3 * time.Second):
fmt.Println(" ✓ process-payment: charged")
return nil, nil
case <-ctx.Done():
fmt.Println(" ✗ process-payment: interrupted before it finished")
return nil, ctx.Err()
}
Run it¶
cd examples/terminate-end-event && go run .
The payment branch starts charging; the fraud branch fires the terminate; the
payment context is cancelled before it can finish, and the instance settles in
Terminated:
⚠ fraud-check: fraudulent order detected — terminating the process
→ process-payment: charging the card (takes ~3s)...
InstanceState Terminating instance_id=…
✗ process-payment: interrupted before it finished
InstanceState Terminated instance_id=…
✓ terminate-end-event finished (Terminated): the fraud branch hit a Terminate
End Event and ended the whole instance before the payment completed
The two branches run concurrently, so
fraud-checkandprocess-paymentmay print in either order. What is deterministic is the outcome: the payment is interrupted and the instance endsTerminated.
Methods & runtime behavior¶
The engine drives the end event; you rarely call these directly. The two that matter for a terminate:
| Method | Role |
|---|---|
EndEvent.Exec(ctx, re) ([]*flow.SequenceFlow, error) |
consume the arriving token; a terminate trigger makes it collapse the scope instead of retiring one token. |
TerminateEventDefinition.Type() flow.EventTrigger |
reports flow.TriggerTerminate — how the engine recognises the trigger. |
Behavior worth knowing:
- One token is enough. The moment a token reaches the terminate end event,
the engine flips the instance to
thresher.StateTerminating, cancels thecontext.Contextof every other active track, then settles the instance inthresher.StateTerminated. It does not wait for the other branches to finish. - Terminated is not Completed. A normal instance that finishes all branches
ends
thresher.StateCompleted; a terminate collapses it tothresher.StateTerminated. Read the terminal state from the handle to tell them apart:
state, _ := h.WaitCompletion(ctx) // blocks to a terminal state
// state == thresher.StateTerminated after a terminate end event
- Cancellation is cooperative. The engine cancels each track's context; a
long-running operation must
selectonctx.Done()to actually stop. An operation that ignores its context runs to completion — its side effects still land — even though the instance is already terminating. - Scope reach. A terminate ends the scope it lives in. At the top level of a process that is the whole instance; inside an embedded sub-process it ends that sub-process's scope and the outer process continues from the sub-process's outgoing flow. See Embedded Sub-Process.
See also¶
- Examples:
examples/terminate-end-event/ - Related guides: Start & End · Parallel (AND) · Exclusive (XOR) · Instance lifecycle
- Design: ADR-001 — Execution model
- Full API:
go doc github.com/dr-dobermann/gobpm/pkg/model/events