Reputation: 323
We have requirements to let our users create their own workflows. Those workflows can have simple yes / no branching as well as waiting for a signal from an external event. This wouldn’t be such a problem if we had well established workflow definition, however since the workflows can be dynamic this poses a much tricker problem.
Upvotes: 4
Views: 392
Reputation: 6870
Temporal Workflows are code that directly implements your business logic.
For use cases when hardcoding the business logic in code is not an option an interpreter of an external workflow definition language should be written. Such language is frequently called DSL as they are really useful when implemented for a specific domain. The DSLs are frequently YAML/Json/XML based. Sometimes it is just data in DB tables.
Here is how I would structure the workflow code to support custom DSL:
Here is a sample code for a trivial DSL that specifies a sequence of activities to execute:
@ActivityInterface
public interface Interpreter {
String getNextStep(String workflowType, String lastActivity);
}
public class SequenceInterpreter implements Interpreter {
// dslWorkflowType->(activityType->nextActivity)
private final Map<String, Map<String, String>> definitions;
public SequenceInterpreter(Map<String, Map<String, String>> definitions) {
this.definitions = definitions;
}
@Override
public String getNextStep(String workflowType, String lastActivity) {
Map<String, String> stateTransitions = definitions.get(workflowType);
return stateTransitions.get(lastActivity);
}
}
@WorkflowInterface
public interface InterpreterWorkflow {
@WorkflowMethod
String execute(String type, String input);
@QueryMethod
String getCurrentActivity();
}
public class InterpreterWorkflowImpl implements InterpreterWorkflow {
private final Interpreter interpreter = Workflow.newActivityStub(Interpreter.class);
private final ActivityStub activities =
Workflow.newUntypedActivityStub(
new ActivityOptions.Builder().setScheduleToCloseTimeout(Duration.ofMinutes(10)).build());
private String currentActivity = "init";
private String lastActivityResult;
@Override
public String execute(String workflowType, String input) {
do {
currentActivity = interpreter.getNextStep(workflowType, currentActivity);
lastActivityResult = activities.execute(currentActivity, String.class, lastActivityResult);
} while (currentActivity != null);
return lastActivityResult;
}
@Override
public String getCurrentActivity() {
return currentActivity;
}
}
Obviously the real-life interpreter activity is going to receive a more complex state object as a parameter and return a structure that potentially contains a list of multiple command types.
Upvotes: 4