Почему Kstry называют предпочтительным фреймворком для бизнес-архитектуры?
Сценарии использования Kstry:
Kstry позволяет визуализировать сложные методы вызова в виде наглядных блок-схем. Фреймворк изолирует независимые модели бизнеса и скрывает их взаимное влияние, одновременно предоставляя динамический механизм для организации отношений между моделями.
Пример планирования процессов.
Адрес кода примера планирования процессов.
Поддержка динамического планирования процессов.
Во фреймворке можно настроить параллельное выполнение процессов с помощью свойства open-async=true
. Kstry как параллельную структуру можно использовать в проекте, её гибкость превосходит CompletableFuture.
Необходимо реализовать асинхронную блок-схему:
Код для определения вышеуказанного процесса:
@Bean
public ProcessLink testAsyncFlowProcess() {
StartProcessLink processLink = StartProcessLink.build(ProcessConfig::testAsyncFlowProcess);
InclusiveJoinPoint inclusive01 = processLink
.nextService(CalculateService::atomicInc).name("Task01").build()
.nextInclusive(processLink.inclusive().openAsync().build());
InclusiveJoinPoint inclusive04 = processLink
.nextService(CalculateService::atomicInc).name("Task04").build()
.nextInclusive(processLink.inclusive().openAsync().build());
processLink.inclusive().build().joinLinks(
inclusive01.nextService(CalculateService::atomicInc).name("Task02").build(),
processLink.inclusive().build().joinLinks(
inclusive01.nextService(CalculateService::atomicInc).name("Task03").build(),
inclusive04.nextService(CalculateService::atomicInc).name("Task05").build()
).nextService(CalculateService::atomicInc).name("Task07").build(),
inclusive04.nextService(CalculateService::atomicInc).name("Task06").build()
).nextService(CalculateService::atomicInc).name("Task08").build()
.end();
return processLink;
}
Определение метода службы узла:
@TaskService
public void atomicInc(@ReqTaskParam(reqSelf = true) AtomicInteger atomicInteger) {
int i = atomicInteger.incrementAndGet();
System.out.println("atomicInc... " + i);
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
Выполнение процесса:
@Test
public void testAsyncFlowDemo() {
// 配置文件 вызов
AtomicInteger atomicInteger = new AtomicInteger();
StoryRequest<Void> fireRequest = ReqBuilder.returnType(Void.class)
.trackingType(TrackingTypeEnum.SERVICE_DETAIL).request(atomicInteger).startId("async-flow-demo")
.build();
TaskResponse<Void> result = storyEngine.fire(fireRequest);
Assert.assertTrue(result.isSuccess());
Assert.assertEquals(8, atomicInteger.intValue());
// Вызов кода процесса
atomicInteger = new AtomicInteger();
fireRequest = ReqBuilder.returnType(Void.class)
.trackingType(TrackingTypeEnum.SERVICE_DETAIL).request(atomicInteger).startProcess(ProcessConfig::testAsyncFlowProcess)
.build();
result = storyEngine.fire(fireRequest);
Assert.assertTrue(result.isSuccess());
Assert.assertEquals(8, atomicInteger.intValue());
}
Как видно из вышеизложенного, порядок выполнения процесса следующий:
🟢 После завершения Task01 задачи выполняются параллельно Task02 и Task03. 🟢 Task04 выполняется, после чего параллельно выполняются Task05 и Task06.
🟢 Task07 ожидает завершения Task03 и Task05, затем выполняется.
🟢 Когда выполнены Task02, Task06 и Task07, выполняется Task08.
🟢 Завершение процесса.
Можно использовать компоненты, такие как сервисы, шлюзы и направленные сегменты с условиями для настройки схемы процесса принятия решений, которая также поддерживает динамические процессы. Это позволяет реализовать динамическую систему принятия решений.
Логика AND:
Рисунок AND-логики
Определение и выполнение метода узла:
// Определение метода узла
@NoticeResult
@TaskService
public int plusCalculate(@VarTaskParam int a, @VarTaskParam int b) {
return a + b;
}
// Выполнение
@Test
public void testRuleAndFlowDemo() {
RuleJudgeRequest ruleJudgeRequest = new RuleJudgeRequest();
ruleJudgeRequest.setA(10);
ruleJudgeRequest.setB(5);
ruleJudgeRequest.setC(15);
StoryRequest<Integer> fireRequest = ReqBuilder.returnType(Integer.class)
.trackingType(TrackingTypeEnum.SERVICE_DETAIL).varScopeData(ruleJudgeRequest).startId("test-rule-and-flow-demo")
.build();
TaskResponse<Integer> result = storyEngine.fire(fireRequest);
Assert.assertTrue(result.isSuccess());
Assert.assertEquals(15, (int) result.getResult());
}
🟢 При выполнении всех условий происходит действие, в противном случае процесс завершается.
Логика OR:
Рисунок OR-логики
🟢 Если несколько исходящих ветвей шлюза анализируются как истинные, выбирается первая истинная ветвь, а остальные игнорируются. Таким образом, можно реализовать логику OR, добавляя условия к исходящим ветвям.
🟢 Можно определить выражения с помощью формата o{число}: выражение
, чтобы указать порядок оценки исходящих ветвей, что позволяет реализовать логику if... else if... else if... else...
.
🟢 Также можно реализовать процесс по умолчанию, который выполняется, когда ни одно из предварительных условий не выполнено. Этот процесс можно рассматривать как NONE.
Выполнение при удовлетворении N условий:
Рисунок выполнения при удовлетворении N условий
testRuleCompletedCountFlowDemo
🟢 Процесс может продолжаться, если количество выполненных задач на шлюзе достигает указанного числа. Указанное число должно быть больше нуля и меньше или равно количеству задач на входе шлюза.
Используя возможности фреймворка для организации процессов, можно интегрировать микросервисы. В сочетании с RPC, HTTP и другими клиентскими технологиями, StoryBus и его свойства task-params
, пользовательские команды и преобразователи типов позволяют легко передавать и преобразовывать параметры между сервисами.
В качестве примера использования HTTP можно сначала создать метод сервисного узла:
@TaskService(name = "http-post")
public void httpPostAction(ScopeDataOperator dataOperator,
@TaskParam("url") String url,
@TaskParam("result") ResultProperty result,
@TaskParam("data") Map<String, Object> data,
@TaskParam("header") Map<String, String> header) {
if (StringUtils.isBlank(url)) {
return;
}
try {
HttpPost httpPost = new HttpPost(url);
if (data == null) {
data = Maps.newHashMap();
}
httpPost.setEntity(new StringEntity(JSON.toJSONString(data), ContentType.APPLICATION_JSON));
if (MapUtils.isNotEmpty(header)) {
header.forEach((k, v) -> {
if (StringUtils.isAnyBlank(k, v)) {
return;
}
httpPost.setHeader(k, v);
});
}
CloseableHttpResponse response = httpClient.execute(httpPost);
String r = EntityUtils.toString(response.getEntity());
log.info("HttpActionService httpPostAction success. url: {}, header: {}, data: {}, response: {}, result: {}", url, header, data, response, r);
if (StringUtils.isBlank(r)) {
return;
}
if (result == null) {
return;
}
noticeResult(dataOperator, result, r);
} catch (Exception e) {
log.error("HttpActionService httpPostAction error. url: {}, header: {}, data: {}", url, header, data, e);
throw new BusinessException("-100", e.getMessage(), e);
}
}
private void noticeResult(ScopeDataOperator dataOperator, ResultProperty resultProperty, String result) {
if (StringUtils.isBlank(resultProperty.getTarget()) ||
``` ```
!ElementParserUtil.isValidDataExpression(resultProperty.getTarget())) {
return;
}
JSONObject jsonObject = JSON.parseObject(result);
Object resData = jsonObject.get("data");
if (resData != null) {
jsonObject.put("data", typeConverterProcessor.convert(resultProperty.getConverter(), resData, Optional.ofNullable(resultProperty.getType())
.filter(StringUtils::isNotBlank).map(className -> {
try {
return Class.forName(className);
} catch (Exception e) {
log.error("HttpActionService convert. type invalid. type: {}", className, e);
}
return null;
}).orElse(null)
).getValue()
);
}
dataOperator.setData(resultProperty.getTarget(), jsonObject);
Порядок разработки микросервисов:
🟢 В процессе выполнения сначала выполняется операция входа в систему.
{
"url": "http://127.0.0.1:8787/login", // URL для доступа
"result": {
"target": "var.login", // Позиция уведомления о результате в StoryBus
"type": "java.util.HashMap" // Тип возвращаемого результата
},
"data": {
"username": "admin", // Данные запроса POST, могут быть константами или переменными
"password": "admin"
}
}
🟢 На втором этапе выполняется запрос ресурсов.
{
"url": "http://127.0.0.1:8787/queryStudent",
"result": {
"target": "var.student",
"converter": "map-to-student" // Результат запроса преобразуется в объект Student с помощью преобразователя типов
},
"header": {
"Authorization": "@var.login.data.token" // Из результата входа извлекается токен и помещается в заголовок для аутентификации
},
"data": {
"id": "@req.id"
}
}
Процесс поиска информации о баллах учащихся, реализованный с использованием микросервисной архитектуры:
🟢 При необходимости можно реализовать пользовательские распределённые транзакции с помощью перехватчиков подпроцессов.
🟢 Фреймворк также поддерживает определение методов узлов службы с использованием подхода Reactor, что позволяет отправлять запросы и сразу освобождать рабочие потоки, а затем выполнять обратные вызовы после получения ответа. Текст запроса написан на языке Java.
Результат:
result, r);
return null;
} catch (Exception e) {
log.error("HttpActionService async httpPostAction error. url: {}, header: {}, data: {}, response: {}, result: {}", url, header, request.getBody().getBodyText(), response, r);
throw new RuntimeException(e);
}
});
} catch (Exception e) {
log.error("HttpActionService async httpPostAction error. url: {}, header: {}, data: {}", url, header, data, e);
throw new BusinessException("-100", e.getMessage(), e);
}
}
Это фрагмент кода, который содержит обработку исключений в методе async httpPostAction
класса HttpActionService
.
В тексте запроса используется несколько переменных и методов, но их значения и реализация не раскрываются.
Также в запросе есть пример использования метода gotoSchool
, который принимает три параметра и возвращает логическое значение. Однако контекст использования этого метода также не раскрывается. Стори реквест с маппингом <Map<String, Object>> файр реквест = Реq билдер.<Map<String, Object>> резалт тип (Мап класс).
рекол стори хук (Веб утилл::рекол стори хук). траккинг тип (Траккинг тип эн юм. СЕРВИС_ДЕТАЙЛ). реквест (реквест). старт ид («http студэнт скор кью процесс»)
резалт билдер ((рес, кью) -> {
Мап<Стринг, Объект> мап = Мапс. нью хэш мэп;
кэйс форэч (кэй -> мап. пут(кэй, кью. дата(кэй)));
рэтурн мап;
}). билд();
Моно<Мап<Стринг, Объект>> файр асинк = стори энжин. файр асинх (файр реквест);
рэтурн Веб утилл. дата декорэйт (реквест, файр асинк);
}
🟢 резалт билдер
— это функция обратного вызова, которая позволяет обрабатывать результаты после завершения процесса. Она принимает два параметра:
🔷 res
: фактический результат, возвращённый из процесса.
🔷 query
: объект ScopeDataOperator.
После модификации клиент может получить нужные данные:
!Получение нужных данных
Платформа предоставляет возможность определять роли с разными уровнями доступа и использовать их для выполнения одного и того же процесса. Это позволяет предоставлять персонализированные услуги тысячам пользователей.
Определение общего процесса
!Процесс с ролевым доступом
Определение методов сервисных узлов
@TaskComponent(name = "orderService")
public class InnerOrderService implements OrderService {
@Override
@TaskService
@NoticeVar(target = CommonFields.F.price)
public long calculatePrice(long goodsId) {
System.out.println("InnerOrderService calculatePrice...");
return 100L;
}
@Override
@TaskService
@NoticeVar(target = CommonFields.F.lockStockResult)
public boolean lockStock(long goodsId) {
System.out.println("InnerOrderService lockStock...");
return true;
}
@Override
@NoticeResult
@TaskService
public long geneOrderId(long price, long goodsId) {
System.out.println("InnerOrderService geneOrderId...");
return 2987;
}
}
Обычный способ выполнения
@Test
public void testRbacFlowDemo() {
InScopeData varScopeData = new InScopeData(ScopeTypeEnum.VARIABLE);
varScopeData.put(CommonFields.F.goodsId, 10);
StoryRequest<Long> fireRequest = ReqBuilder.returnType(Long.class)
.trackingType(TrackingTypeEnum.SERVICE_DETAIL).varScopeData(varScopeData).startId("test-rbac-flow-demo")
.build();
TaskResponse<Long> result = storyEngine.fire(fireRequest);
Assert.assertTrue(result.isSuccess());
Assert.assertEquals(100L, varScopeData.get(CommonFields.F.price));
Assert.assertEquals(2987L, (long) result.getResult());
}
// Журнал печати
// InnerOrderService calculatePrice...
// InnerOrderService lockStock...
// InnerOrderService geneOrderId...
Добавление расширенных возможностей
@TaskComponent(name = "orderService", scanSuper = false)
public class ExternalOrderService extends InnerOrderService {
@Override
@NoticeVar(target = CommonFields.F.price)
@TaskService(ability = "external")
public long calculatePrice(long goodsId) {
System.out.println("ExternalOrderService calculatePrice...");
return 200L;
}
@Override
@NoticeVar(target = CommonFields.F.lockStockResult)
@TaskService(ability = "external")
public boolean lockStock(long goodsId) {
System.out.println("ExternalOrderService lockStock...");
return false;
}
}
🟢 @TaskService
с параметром ability = «external»
добавляет новую точку расширения к сервису calculatePrice
.
Назначение ролей и распределение прав доступа
@Component
public class AllocateRoleConfig implements DynamicRole {
@Override
public Optional<Role> getRole(String key) {
if (Objects.equals("test-rbac-flow-demo@external-business-id", key)) {
ServiceTaskRole serviceTaskRole = new ServiceTaskRole();
serviceTaskRole.addPermission(PermissionUtil.permissionList("r:calculatePrice@external"));
``` **Создание экземпляра, реализующего интерфейс DynamicRole и передаваемого для управления Spring-контейнеру, позволяет реализовать динамический распределитель ролей.**
**Если при обработке запроса startId и businessId совпадают, создаётся роль с настраиваемыми правами доступа.**
### Расширение бизнес-логики на основе сопоставления ролей
```java
@Test
public void testRbacFlowDemo() {
InScopeData varScopeData = new InScopeData(ScopeTypeEnum.VARIABLE);
varScopeData.put(CommonFields.F.goodsId, 10);
StoryRequest<Long> fireRequest = ReqBuilder.returnType(Long.class)
.businessId("external-business-id")
.trackingType(TrackingTypeEnum.SERVICE_DETAIL).varScopeData(varScopeData).startId("test-rbac-flow-demo")
.build();
TaskResponse<Long> result = storyEngine.fire(fireRequest);
Assert.assertTrue(result.isSuccess());
Assert.assertEquals(200L, varScopeData.get(CommonFields.F.price));
Assert.assertEquals(2987L, (long) result.getResult());
}
// Логи выводится в консоль:
// ExternalOrderService calculatePrice...
// ExternalOrderService lockStock...
// InnerOrderService geneOrderId...
При выполнении сервисного узла, если на основе роли найдено расширение бизнес-логики, то оно выполняется вместо стандартной логики. Если соответствие не найдено, используется стандартная логика.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )