You use state machine transitions, triggers, and guards to define which lifecycle changes an object can make and when users or code can make them.
A state machine defines the allowed states of an object and the transitions between them. For example, a production item can move from Started to OnHold or Finished, but it cannot move directly from Finished back to Started unless you model that transition.
The state machine stores its current value in a state attribute. A state attribute is a string attribute managed by the state machine. The state diagram is the authoritative list of allowed values and allowed paths between them.
State machine parts
| Part | Purpose | Example |
|---|---|---|
| State | A permitted lifecycle position for an object. | Started, OnHold, and Finished
|
| Transition | An allowed directed path from one state to another. | Started â Finished
|
| Trigger | A named method on the class that attempts a transition. | Finish
|
| Guard | An OCL expression that must evaluate to true before the transition is allowed.
|
self.DepositPaid
|
| Entry action | An action that runs when the object enters a state. | Create a transfer document when a sale enters InProgress.
|
| Exit action | An action that runs when the object leaves a state. | Clear a temporary reservation when an item leaves OnHold.
|
Model a transition
In MDriven Designer, create a state machine attribute on the class. MDriven Designer creates a state diagram for that attribute. Add states, then use the transition tool to draw the allowed paths between them.
For a ProductionItem class, you might define these paths:
StartedâOnHoldStartedâFinishedOnHoldâStarted
When a new object is created, it follows the initial transition. If that transition has no guard preventing it, the object reaches the first operational state. For example, an item can move from the initial state to Started when it is created.
A class can have multiple state attributes and therefore multiple state machines. Use this when an object has separate lifecycles from different perspectives, such as LifeTimeState and CleanlinessState. State names must be unique within the class so that an expression such as self.oclIsInState(#Started) is unambiguous.
Add and use triggers
A trigger is the entry point that attempts a transition. Add a trigger to a transition and give it a name that describes the requested change.
For example, add these triggers to ProductionItem:
| Current state | Trigger | Target state |
|---|---|---|
Started
|
PutOnHold
|
OnHold
|
Started
|
Finish
|
Finished
|
OnHold
|
Resume
|
Started
|
The trigger becomes a method on the class. You can expose that method as an action in a ViewModel or invoke it from application logic. The trigger is available only when the object is in a state where its transition is valid and its guard permits the transition.
Use names that describe the requested operation, such as StartConstruction, Finish, or Demolish. Avoid names that only repeat implementation details.
Control transitions with guards
A guard is an OCL expression on a transition. The guard must return true, or be empty, for the transition to be available.
For example, a car must not enter production until its deposit is paid and its brand is set:
self.DepositPaid and self.TheBrandOfTheCar->notEmpty
Put this expression on the transition to InProduction. The StartProduction trigger is unavailable until both conditions are true.
A guard should answer one question: may this object take this transition now? Keep calculations and side effects out of guards. Put work that must happen after a transition in an entry action, exit action, or transition effect.
Reuse class constraints as guards
Use GuardConstraints when a transition depends on one or more existing class constraints. This avoids copying the same rule into several guards.
For example, define constraints on Order that require a customer and delivery address. Associate both constraints with the Confirm transition. The transition remains disabled until both constraints are valid.
To associate constraints with a transition:
- In the state diagram, right-click the transition.
- Select Edit GuardConstraintsâŚ.
- Select the class constraints that must be valid for the transition.
GuardConstraints also provide better feedback than a standalone guard expression because constraints have messages. Users can see which validation rules prevent the trigger from becoming available.
Re-evaluate guard-driven transitions
Use stateMachineTrigger when your state machine follows guards rather than named triggers. This OCL operator asks an object to re-evaluate all untriggered transitions from its current state and take a transition whose guard now permits it.
self.stateMachineTrigger
For example, an Order has an untriggered transition from WaitingForPayment to ReadyToShip with this guard:
self.PaymentReceived
After setting PaymentReceived to true, evaluate self.stateMachineTrigger. MDriven re-evaluates the guards from WaitingForPayment and moves the order to ReadyToShip if the guard is true.
Use named triggers when an actor explicitly requests a change, such as PutOnHold. Use stateMachineTrigger when changed data should drive the next state automatically.
Put work in entry actions or transition effects
You can define actions that run as part of a transition.
- Use an entry action when the work belongs to arriving in the target state, regardless of which transition enters it.
- Use an exit action when the work belongs to leaving a state.
- Use a transition effect when the work belongs only to one specific path.
For example, when a car sale enters InProgress, an entry action can create a transfer-ownership document and assign it as the current transaction. When the sale enters Closed, an entry action can transfer the car from the seller's collection to the buyer's collection.
If Closed can be entered through several transitions that need different work, put that work on the individual transitions instead of in a shared entry action.
State machine rules are enforced by the framework
You cannot move an object through an undefined transition through the MDriven framework. For example, if the model allows State1 â State2 â State3, application logic cannot move directly from State1 to State3.
Do not update the state attribute directly in the database. Direct database updates bypass the framework and can cause problems with MDriven Server synchronization and history configurations because the change is not recorded through the framework.
