Как создать пользовательские определения рабочих процессов?

У нас есть требования, чтобы позволить нашим пользователям создавать свои собственные рабочие процессы. Эти рабочие процессы могут иметь простое ответвление "да / нет", а также ожидание сигнала от внешнего события. Это не было бы такой проблемой, если бы у нас было точное определение рабочего процесса, однако, поскольку рабочие процессы могут быть динамичными, это представляет собой гораздо более сложную проблему.

1 ответ

Решение

Рабочие процессы Cadence - это код, который непосредственно реализует вашу бизнес-логику.

Для случаев использования, когда жесткое кодирование бизнес-логики в коде не является опцией, должен быть написан интерпретатор языка определения внешнего рабочего процесса. Такой язык часто называют DSL, поскольку он действительно полезен при реализации для определенного домена. DSL часто основаны на YAML/Json/XML. Иногда это просто данные в таблицах БД.

Вот как я бы структурировал код рабочего процесса для поддержки пользовательских DSL:

  1. Операция, которая получает идентификатор и состояние определения текущего рабочего процесса и возвращает список операций, которые нужно выполнить. Это действие применяет текущее состояние (которое включает результаты к последним выполненным операциям) к соответствующему экземпляру DSL. Результатом является набор следующих операций для выполнения. Операции зависят от DSL, но наиболее распространенными являются операции выполнения, ожидания определенного сигнала, ожидания в течение некоторого времени, завершения или сбоя рабочего процесса.
  2. Рабочий процесс, который реализует цикл, который вызывает вышеуказанное действие и выполняет запрошенные операции, пока не будет запрошена операция завершения рабочего процесса.

Вот пример кода для тривиального DSL, который определяет последовательность действий для выполнения:

public interface Interpreter {
  @ActivityMethod
  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);
  }
}

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;
  }
}

Очевидно, что реальная деятельность интерпретатора будет получать более сложный объект состояния в качестве параметра и возвращать структуру, которая потенциально содержит список из нескольких типов команд.

Другие вопросы по тегам