← все видео

Dapr Workflows for Spring Boot 4 Developers

Diagrid · 2026-04-02 · 52м 2с · 54 просмотров · YouTube ↗

Топики: durable-execution

🎧 Аудио

📝 Summary

model=deepseek-v4-flash · prompt=summary-v7 · 12 471→2 325 tokens · 2026-07-20 15:05:29

🎯 Главная суть

Dapr Workflows позволяют описывать долговременные, отказоустойчивые оркестрации в коде на Java (Spring Boot), делегируя их выполнение durable-движку внутри Dapr sidecar. Интеграция с Spring Boot 4 через стартеры и Testcontainers даёт возможность разрабатывать и тестировать такие оркестрации локально без Kubernetes, а в production запускать тот же код на кластере.

Как работают Dapr Workflows

Workflow определяется как класс, реализующий интерфейс Workflow из Java SDK. В методе create описываются шаги — вызовы активностей (WorkflowActivity), ожидание внешних событий, таймеры, условная логика. Каждая активность — это единица долговременного выполнения: если приложение крашится во время выполнения активности, после перезапуска рабочий процесс продолжается с того же места, не повторяя уже завершённые шаги. Движок, работающий внутри Dapr sidecar, отвечает за сохранение состояния, повторные попытки при ошибках и координацию.

Интеграция со Spring Boot 4

Для включения Dapr в Spring Boot приложение достаточно добавить два стартера:

После добавления зависимости в pom.xml и аннотации @EnableDaprWorkflows на классе приложения Spring автоматически сканирует все @Component‑классы, реализующие Workflow или WorkflowActivity, и регистрирует их в durable-движке.

Локальная разработка с Testcontainers

Чтобы не разворачивать полноценный кластер Kubernetes, используется интеграция с Testcontainers. Достаточно создать DaprContainer и сконфигурировать его: имя приложения, порт, на котором Spring Boot слушает callback (чтобы sidecar мог вызывать активности), а также компоненты инфраструктуры. Например, для хранения состояния workflow требуется state store — в демонстрации используется PostgreSQL, который тоже запускается как Testcontainers‑модуль. Dapr автоматически настраивается на использование этого PostgreSQL. Также автоматически поднимаются:

Таким образом, разработчик получает полностью функциональное окружение без ручной установки Dapr.

Структура простого workflow

Пример из видео содержит workflow DemoChainWorkflow (35 строк). В методе create:

  1. Получается идентификатор экземпляра и входные данные.
  2. Вызов первой активности FirstActivity с await — результат преобразуется в верхний регистр.
  3. Условная ветка: если исходное содержимое содержит слово "hello", вызывается вторая активность, добавляющая три восклицательных знака.
  4. Устанавливается выходное значение через ctx.complete().

Каждая активность — @Component, реализующий WorkflowActivity. Внутри активности можно вызывать любые сервисы, делать HTTP‑запросы, обращаться к базам данных. При ошибке движок повторяет выполнение активности согласно настройкам retry.

Запуск приложения и демонстрация

При старте Spring Boot Run без Testcontainers приложение не может подключиться к sidecar — в логах видна попытка соединения и отказ. Если же запускать с тестовой конфигурацией (через @SpringBootTest или отдельный test‑профиль), Testcontainers поднимают все контейнеры, приложение подключается и workflow‑клиент готов принимать запросы.

В демонстрации отправляются два POST-запроса на /start:

После выполнения в Workflows Dashboard видны оба экземпляра, время выполнения (92 мс), события по каждому шагу, входные и выходные данные активностей.

Паттерны и расширенные возможности

В репозитории Diagrid Labs Workflow Patterns Spring Boot собраны примеры:

Тестирование workflow

Spring Boot и Testcontainers позволяют писать полноценные интеграционные тесты. В тестовом классе:

Такой тест гарантирует не только корректность логики workflow, но и правильное взаимодействие с внешними сервисами (через моки).

Интеграция с Spring AI и проблема долговечности

Видео демонстрирует типичный сценарий параллельных запросов к LLM. В примере Spring AI используется Executors.newFixedThreadPool(4) для отправки одного и того же промпта четырём группам (customers, employees, investors, suppliers). Проблема: если приложение упадёт во время ожидания, все завершённые запросы придётся выполнять заново (потеря результатов и потраченных токенов).

Решение с Dapr Workflows: каждая отправка промпта помещается в отдельную активность. Workflow запускает четыре активности параллельно (без await на каждой), затем ждёт все через context.whenAll(...). Если крах происходит после завершения двух активностей, при перезапуске движок возобновляет выполнение с точки ожидания — уже завершённые активности не перезапускаются. Экономия токенов и времени.

Производственное развёртывание и дополнительные интеграции

Для запуска в production рекомендуется платформа Diagrid Catalyst, которая предоставляет готовые управляемые Dapr‑сервисы, мониторинг, алерты, UI визуализации workflow и метрики производительности. В разработке находятся интеграции с Quarkus, а также поддержка аннотации @Scheduled для регистрации асинхронных задач через Dapr — чтобы Spring Boot разработчики могли использовать привычный синтаксис, а под капотом работал durable движок.

📜 Transcript

en · 9 220 слов · 114 сегментов · clean

Показать текст транскрипта
My name is Mauricio Salatino and today we will be talking a little bit about DAPR workflows with Spring Boot applications. I think that this is a very interesting topic for me because DAPR provides a lot of different functionalities and that's why today we are just going to be focusing on doing a little bit of a deep dive on how workflows work when working in the context of Spring Boot. If you're watching this video, I'm assuming that you know what Dapr is and how Dapr works on top of Kubernetes. But this session is specifically for developers using the Spring Boot framework, which is a Java framework, and how Dapr integrated with this framework to make a nice, really nice developer experience. So let's get started by sharing some links. I've done some presentations before in different, you know, Java conferences about these integrations. so feel free to check the blog post that is called dapper for springboard that explains a little bit about what's the integration about and how it works but it's general for the entire like set of functionalities about dapper while this presentation is more about dapper workflows dapper for java developers is another article that it's a little bit more generic and explains why dapper can complement some of the java frameworks to you know have a better experience with building distributed applications that will run on top of Kubernetes. And as I mentioned before, we have done several sessions in different conferences. These are links for Spring IO. The recordings are on YouTube. A presentation that we did with Thomas trying to explain how Dapper and Spring would complement each other and how delegating some of the responsibilities to the infrastructure will simplify your Spring Boot applications. while i did a presentation last year with laurent from microx about how to test uh you know like distributed applications using dapper using microx and how these applications will run on top of kubernetes after you build all these integration tests this content is all available online so i recommend you to simplify just to check this on youtube and if you're interested feel free to reach out so let's talk about DAPR workflows. Again, this session, it's not about all the DAPR APIs, it's specifically about DAPR workflows. The DAPR workflows, what we can do is we can rely on the DAPR sidecar that runs inside of a Kubernetes cluster, very close to your application as an orchestration engine or as a durable engine. The idea here is that you can codify in code or like complex orchestrations that your applications might need. and how this orchestration will be executed will be delegated to the sidecar functionality that runs the durable engine. As you can see here, we are using Java to define a simple workflow. It's called DemoChainWorkflow. And you do that by implementing an interface that it's called Workflows that comes with the Java SDK. In order to implement the logic of the workflow, you basically implement this create method and inside you define what are the steps that your workflow is going to be executing. And inside this code, basically you have different ways to create durable code, code execution. In this case, by just calling activities, you implement another interface that is called workflow activity that we will see in code. And that basically will represent your unit of durability. The main idea behind this is that if you have complex orchestrations, like your application calling tens of thousands of different services, you want to make sure that if something breaks inside the application, the application, when it restarts, and let's say that the pod gets killed for some reason, when it restart, it continues from where it left off. The main idea here is to build resiliency inside your applications and make sure that these complex orchestrations and interactions between services is being handled by in this case the durable engine that runs inside the upper sidecar so this workflow example is very simple it calls three different activities in a chain so the idea is you call the first activity that is implemented by this class you wait for the activity to finish and then move to the next activity right again if something crashes if the application crashes while you're executing this activity when the application restarts the workflow will continue from where it left off and it will finish it till completion That's the whole point of having a durable engine. In order to make this experience much nicer for Spring Boot developers, because I've mentioned Kubernetes, I mentioned installing Dapr inside a Kubernetes cluster, we build this Spring Boot and Test Container integration that basically allows Spring developers not to worry about Kubernetes, not to worry about Dapr at all, and just to start defining their workflows inside their applications that will be delegated to the Dapr sidecar. but all the wiring will be automatically configured for them. So we built this integration for Spring Boot 3 and now we are supporting Spring Boot 4 as well. The idea here was to simplify the developer experience to make sure that, you know, developers, Spring Boot developers do not need to think about dapper in terms of Kubernetes. They can just keep using their Spring Boot applications as they were used to do for local development. Once their applications are ready to run in production, they can just create a container, deploy that into a cluster and their applications will work there. But while they are developing, we need to configure in some way the DAPR components so the local application can connect to it and it can actually, for example, schedule new orchestrations with the durable engine. We have created integrations for the different DAPR APIs, but as I mentioned before, we are going to focus only on workflows in this session. In order to start using Dapper inside your Spring Boot application, we provide two dependencies that you can add. Like in a very Spring Boot fashion, we provide the Dapper Spring Boot starter that brings all the integration between the Java SDK and the Dapper APIs into the Spring Boot context. So Spring can use these APIs in a very Spring Boot way. And then the second part, which I think it's extremely important to have a very... good developer experience for like local development is the testing part, which brings test containers into the picture, right? So you have two dependencies, the normal Spring Boot starter that it's needed for like creating applications and running them in production, but then the test dependency that will simplify the local developer experience. So let's take a look into that. So in order to provide like a test containers integration, what we did is we implemented this container, this test containers module that basically allows us to set up whatever we need from the DAPR context to make sure that we can start a DAPR sidecar very close to our application, plus all the control plane components that DAPR usually install inside Kubernetes clusters. We need to have like the minimal setup for enabling developers to develop their applications against this setup. instead of thinking in terms of, oh no, I just need to package my application and deploy it into a cluster to be able to test it. That's too complicated, we wanted to make it simple. So right now the test containers approach is that you create this, in this case, this DAPR container instance, and you configure it depending on which APIs you want to use from DAPR. And this will automatically bootstrap all the DAPR containers whenever you start your Springwood application in, let's say, developer mode. or in test mode with the test context. So you can see we are using some of the Spring Boot annotations here like service connections that allows us there to register to the application, which has a client to connect to the Dapper APIs, where that Dapper container is running and where the services can be located. So the application and the Dapper setup can be connected automatically and magically for the user. So they don't need to even worry about where those containers are running. In general, you need to think about in terms of the Spring Boot application that you're going to start. And because it's already integrated with Dapr by adding these dependencies, the Dapr sidecar and all the Dapr control plane services is going to be automatically started for you. So you can actually write tests against these services and connect to all the Dapr APIs. You see here on the right side, the infrastructure block, which basically is one of the main things that Dapr can do for you, which provide an abstraction to infrastructure. And in this case, what we're showing is that you can configure Dapr to connect to whatever infrastructure is available, right? So you start your Spring Boot application, Dapr gets configured and it automatically binds or connect to infrastructure that you provide to your application. Again, we are using normal Spring Boot and Test Containers, like the Test Containers approach to configure Dapper to work correctly for your application, including all the configuration required for workflows to be able to run at scale. And what I'm showing here is a little bit of more complicated setup. In this case, I was trying to use the Dapper PubSub API, and for that, I needed a message broker. and I'm showing here how can I configure quickly RabbitMQ with test containers. This is just test container module that will start RabbitMQ for me. What I'm doing here on the DAPR configuration side is basically connecting my RabbitMQ instance with DAPR so I can use the DAPR Pubsal APIs and not worry about connecting to RabbitMQ directly. This is kind of interesting if you look into the application class path when you're doing these kind of setups. I do not have the RabbitMQ client in the context of Java. I'm just delegating that connection to happen on the Dapr sidecar. You can go and take a look into the docs if you're interested in that specifically, but usually that's the way that Dapr works, removing and moving the clients that will deal with connections into the sidecar instead of keeping that inside of the Spring Boot application context. But if you look into workflows, we have taken an extra step to make workflows and workflow authoring to feel more Spring native. And in this case, what I'm showing here is another very simple example that basically uses the call activity and wait for an external event to create a very simple flow where we register a customer, wait for an external event from the customer reaching out, and then we will execute a customer follow-up activity. whenever that happens. You can see that we can specify duration of how much do we need to wait for that event to happen and then we can define what happens if that event doesn't arrive in the first five minutes. But importantly enough and more related to Spring Boot is both workflows and workflow activities are you know are Spring bins that are going to be managed by Spring and they're going to be discovered by the Spring runtime and automatically registered. to the sidecar to the upper sidecar for orchestrations that provides a lot of different advantages in this case i'm showing a workflow activity that basically allows us to auto wire any other spring bin that you have inside your application so in this case we have a customer store so you can imagine that you will you already have a lot of different services inside your spring with applications and a lot of let's say clients to connect to remote services what we are doing here is we are defining a workflow activity that wraps that logic into a durable execution unit. So what's happening here is we are calling the customer store add customer operation inside an activity that if for some reason it fails, it will be retried automatically. And if it was executed successfully, the workflow will continue from the next activity instead of re-executing this code again. And that's very important nowadays if you are calling LLMs that cost money and we waste tokens every time that we call an LLM. Having this durability in that space makes a lot of sense because it will actually save us a lot of money. And finally, from the consumer point of view, from the user point of view, if we want to, if we already define all of our workflows and now we want to start scheduling new instances of these workflows. It's pretty common that these kind of solutions are used in large scale scenarios where you have tons of customers scheduling a lot of new workflow instances in parallel at the same time. And these are the APIs that Dapper Workflows exposes for us to schedule these workflows. So you can see we have a Dapper Workflow client that of course it's auto wired by Spring. It just creates a new instance that connects to the the specific DAPR runtime that we have in this case, started by test containers or a remote DAPR instance. And basically it allows us to schedule new workflows, which basically the only thing that it does, it basically requests to the DAPR sidecar to trigger a new orchestration for this specific workflow and quickly return an ID that we can return to the customer to then just keep track or send events, for example, like race events to that specific instance. The important thing to notice here is that we can actually go and return because the workflow execution, the workflow instance is going to be scheduled by the DAPR sidecar and then the DAPR sidecar is going to call our application back whenever it needs to execute an activity. As I mentioned before, we need both workflows and activities to be Spring Bins and by adding this annotation to our application, enable DAPR workflows, what we are telling the application is to go and scan all the workflows. and automatically register all workflows and activities to the workflow runtime. In this case, that it's located in the sidecar. All that magic is happening behind the covers. You don't really need to know about that as a user, but you need these three components. You need enable dapper workflows inside your applications, you need the dapper workflow client to trigger new workflows, and then you need workflows and workflow activities to find the orchestrations that you need to run. Of course, With workflows, it's very common like with durable execution engines and workflow engines, you can create different patterns, different combinations on how do you trigger activities and how do you wait for events. We will look a little bit into parallel tasks like the parallel execution model. We will look a little bit into, not into child workflows, but this is just showing that workflow definitions can be reused. into other workflows by just calling a child workflow. And that will create a relationship between these executions that can be used to track back of what happened and what data was used in the parent workflow to then is being sent to the child workflow as well, providing kind of like a full traceability about what happened in your business workflows. And of course, there are more patterns there. But what I wanted to also mention is that during this demo, I will be showing that I read a workflows dashboard. that allows us to see all the workflow executions that we have run in our inside of our applications and this was designed for like development purposes right like when you are running workflows it's really nice to have a ui where you can go and check what happened with each workflow instance that was executed how much time did it take and if something went wrong you want to see things failing there or things pending right So let's take a look into the example that I have for today. Again, it's a simple example, but I wanted to go over the details, like trying to do like a more deep dive about the pieces that you need in order to make a Spring Boot 4 application work with DAPR workflows in this case. So I'm switching here to my IDE and I hope you can see my screen. And what you see here is just the Spring Boot 4.0.5. This is very like the latest version of Spring Boot. And I've created this POM file, like the entire application I created using start.spring.io, right? Like you can go and create your project there. And then basically I added the dependencies that I mentioned before. I added a Dapper Spring Boot starter test for testing as testing dependency. And I added the Dapper Spring Boot starter for being able to define my workflows and register them into the Dapper sidecar. So let's take a look. at a very simple workflow definition just to get started right so i created this very simple workflow definition that has how many lines it has 35 lines right simple things and i've included uh like an if statement that it's not really needed but i wanted to show a couple of things that you can do with workflows and how you create them as i mentioned before the first thing you do is you implement the workflow interface that's the first thing that you need to do And you need to mark this as a Spring component so it gets automatically scanned as a Spring bin and it can be detected by the enabled dapper workflow annotation that I mentioned before. We will see that in a bit. The next thing that you need to do is you need to implement this method that's defined in the interface that's called create that basically has the logic of the orchestration that you want to define. And you can do a couple of things here. First of all, you can get the instance ID. So every time that you create a new instance of this workflow, you will have a new ID. That's why we are logging that. And then you can get an input, right? So each instance, the difference between workflow instances, in this case, the same workflow instances, is the data that they will manage, right? And in this case, you can get the input data that you're getting from the user. right like let's say that i'm creating orders well the input data will be the order id plus the details of that order so the workflow can do something about it in this generic example i have an object this is a normal java record that has a content field inside it just a string right so we get an input whenever we start an instance and then we can do stuff with that in One of the things that we can do is we can call an activity. In this case, it's called first activity and this implements the workflow activity interface. The workflow activity interface also pushes you to implement one method that's called run. That is basically defining which code we want to run in a durable unit in this case. And the code is very simple. It basically takes the input from that activity and then it just makes it uppercase. and it then returns a new payload object back to the workflow so it can be used into the next activity. As I mentioned before, you need to mark these objects as components so they get automatically registered to the Durable Engine automatically for you as a user. Let's go back to the workflow. You can see here we are calling the first activity. Remember what I mentioned before, await is extremely important here if you want to model that behavior. You want to call an activity, wait until it's done because you want to get the result and do something with that result of that execution. Inside first activity or inside any workflow activity, as I mentioned before, it's very common to have like a service to service request. Like you might want to call an external API, you might publish a message, you might call a different workflow, you might integrate with an LLM or whatever your application is doing. right but again it defines that unit of execution and retryability that will be executed automatically by the workflow engine so if for some reason the first activity fails it throws an exception the workflow engine will just try to retry that several times that can be configured also with the apis but automatically out of the box it will keep retrying until it exhausted all the retry uh all the retry numbers that are configured So imagine that you call this activity, you get the result. In this case, the result is just changing the content of the payload into capital letters. And then we have an if statement. This is just to demonstrate that you can implement any logic that you want. You can have a for loop, an if statement, a switch loop, just to define the logic of this orchestration. Imagine that you're calling a service, depending on the output of that service, you want to go and call a second service. In this case, we are just evaluating the content of the original payload. If the original payload contained the word hello, then we are going to call this second activity. This second activity is exactly the same. It's very simple. It just adds three exclamation marks into the payload, and it returns a new payload that we can use to do something else. Finally, it's super important to see this line here, CTX complete, that sets an output. right like so in this case after executing the second activity or if we didn't execute the second activity we want to complete the workflow execution right both cases we are setting a message of completion which is basically the payload that we want to mark as the result of the workflow and it's a string plus the result resulting content after executing the activities right so depending on where this workflow execution goes is the output that we will get right So we have the dependencies, right? So we have the dependencies, we have the workflow definition and we have the activities. The last part is to create like the endpoint, right? So to expose this to the user, right? Like users should be able to start workflows, maybe by calling an endpoint, maybe by interacting with the UI. In this case, I wanted to keep it as simple as possible. So I auto-wired the Dapper workflow client. This has been auto-wired in a constructor, but I didn't want to show that just yet. and it has like a post request to the start endpoint, right? The request body receives a payload and then we use the workflow client to schedule a new workflow instance and we return automatically the instance ID to the user. So again, when I call this endpoint, we will schedule a new workflow instance into the upper sidecar and automatically return an ID back to the user. You can see that we are taking the payload from the request body and using that as the input for the workflow execution that will be logged here in this line 19 right so we're just going to see every time that we start a new instance we will see workflow started with the instance id and the payload content when the workflow instance started and then we will see a log of the output of that specific workflow depending on which activities were executed Simple, but at the same time, these are the main building blocks that you need to learn in order to start using Dapper workflows. So what I wanted to do here is I wanted to start the application Spring Boot Run. This is how you start Spring Boot applications. Very straightforward. What this is going to do is going to basically start the Spring Boot context. And what you will immediately see here is that there are a couple of... messages here. It says starting workflow runtime, that's the client, but then it tries to connect to the sidecar and it's not available. And you might wonder, okay, what's going on here? The thing here is that I've started the application, but I haven't started DAPR. So in this case, the application will keep trying to connect to the DAPR durable execution engine. And it's not connecting because it's not there. So what I will do is I will kill this application. And now we need to talk a little bit about the testing side of things and the test containers integration that I mentioned before. And that's why we have, you know, these other dependencies here that is for testing purposes. So, so far we have been only using the Dapr Spring Boot starter to define our workflows, to define our activities, and also to register our workflows to the, you know, to the Durable Engine execution that doesn't, it's not running yet. So the application cannot work with this. I didn't show you, but of course, if I start the application and I cannot connect to the, you know, to the durable execution engine, if I call the endpoint here, if I call the endpoint, it will actually fail with the 500, right? Because the execution engine is not there. It's not being able to connect and you will get an exception saying connection refused. Yeah, I cannot connect to the orchestrator right now. So let's stop this and let's talk about testing and local development by using the test containers integration. So the test container integrations works as follow. The only thing that you need to define is a Dapper container. And you can add more things to it depending on the infrastructure that you have available and what are you running inside that, right? So if you're using the PubSub API, you might need a PubSub, like a message broker. If you're using observability, then you might need to configure where is your open telemetry collector. If you're using the state store, you need to define where the state store is running, which is something that we are doing here. We are defining a new Dapper container. That's all you need. You need the name for the application and the port where the Spring Boot application is running so Dapper can connect back to the application if needed. Then what you see here is that we are setting up a state store component, this one here, that is called KVStore and we are using PostgreSQL. to store all the workflow execution data that we need right like in order to make our workflows doable we need to have a state store we're going to store like all the events generated by our workflows so if something goes wrong the workflow engine knows where to resume the execution uh you can see here that we are doing two things defining which state store do we want to use for storing workflows but also setting up the postgreSQL details about like where is this going to be stored in this case in a PostgreSQL instance. These are the connection details to connect to that PostgreSQL instance but we are also using test containers here to start a new instance. Test containers provides different modules for different infrastructure and in this case we are using PostgreSQL. So for that you need a new dependency in this case that I can show you here. So we are adding a new test dependency that test contains PostgreSQL integration. that allows us to start a new PostgreSQL instance for testing purposes. So then we can connect DAPR to that instance. And finally, something that I will show you in a bit is the DAPR workflow dashboard container that also comes with the DAPR Spring Boot Starter and that it also reused the connection to the same state store to access the same data from our workflow execution so we can see what's going on there. Cool. So we have the test configuration. The last bit of this configuration is the test demo application. And this is how you can tell Spring to start your application, but configured for test purposes. It's for two things, for running the application like in development mode, but also it is for just running your tests, your integration tests in this case. So you can see here what we are doing is we are defining a new Spring Boot application inside our test packages. And we are reusing like our demo application from the main package, right? But we are injecting these test containers configuration. So whenever we start this application in test mode or in development mode, we are going to get this containers being automatically provisioned or created for us. So we can see that. You can see here that I'm not running any containers right now, but the moment that I start the Spring Boot application with a different goal, in this case test run, it's going to pick up this configuration and use test containers to start my containers. Let's do that. We'll see here that test containers will kick in at some point and it will start basically creating the containers that are configured in this Dapper test containers configuration class. You see. Dapper being created and the application started. So it will start all the containers, it will start the application and you can see now that the workflow runtime started and it actually connected to the sidecar that is running in this specific port for this specific setup. Remember that with test containers, containers are started in random ports and then there is a mapping happening there that allow my application to know where the container that it's looking for is being created. We can go here back to Docker to see all the containers that were created and we can see that the PostgreSQL instance was created, that the Workflows dashboard was created, the Dapper sidecar was created, and then the control plane services that we need to scale up the workflow creation. Like if we're creating a lot of instances, we need a distributed system and scheduler to be able to handle the load and schedule across different nodes. And the placement service as well is another control plane. service that we need to run here locally for Dapper to work. This is all automatically wired for you. You don't really need to worry about this. Just create your workflow executions and then use these dependencies to start this in local development mode. So the next logical step is to go and send the same request that we sent before. Now that we have the entire setup running, we send the request. You can see super quick, we get back an ID from the workflow execution. and on the logs we will see the logs that we expected right like the workflow was started with that id we get a payload in this case i didn't show you the payload but the payload is this one just like a json file with content and value in this case it's called some payload and it's printed here and then you can see that activities will be executed for that so we are executing the first activity we executed the first activity that basically transformed this string into capital letters and that's what is being printed here as the you know as the workflow output the string right so in this case we executed a very simple workflow that went through a path of the execution of the logic that we defined and we can see the output of that workflow let's do the other payload which is the one that contains hello in this case hello payload again just to trigger the other branch of the logic that we define request hello right so we just run that again same as before we get the id And then we can see that the first activity that makes the capitals letter were executed, but also then the second activity was executed as well, adding the three exclamation marks at the end. Again, super simple example to show how these executions work. And just to finish that part of the example, I wanted to go quickly, scroll up into the logs here because some of the things, one of the things that got started when I started my application is the dapper. Workflows dashboard here that we can open in a new Safari window and we can go here to Workflows and see that we executed two different instances. The first one is with the some payload content and here we can see the output of the entire execution. We can see when it got started, how much did it take, in this case 92 milliseconds which is pretty good and then we can see all the events that happened as part of that execution. You see here like that the execution was started, the orchestrator was, the orchestration was started. There were no issues and the first activity got scheduled and then it was completed, right? If you expand, you can see actually the output of each specific activity that was executed. And in this case, because the content didn't have a hello in the content in the string, we only executed a single activity. We can go and see the same for the second activity. for sorry for the second workflow execution where the content had hello so the output now needed to exit so the workflow now needed to execute two activities in order to get this done you can see the first activity got executed and then with the output of the first activity that was in capital letters we take that output and we use it as the input for the second activity which then add the exclamation marks at the end right so in this case again very simple example you can start now building different workflows, much more complex workflows and interactions based just by using these building blocks. I haven't showed the race event capability, but again, what you can do is you can make a workflow wait for an external event to come in order to trigger new activities in the workflow, which is a very common pattern as well. So good. We have the first part of the example done. And I think that again, focusing on the building blocks, we can talk a little bit about more complex scenarios now. The first thing that I wanted to show you is from the presentation screen. Let me see if I can have my presentation back here. Yes, so I have the presentation back. I wanted to talk a little bit about some things that I mentioned before. So as I mentioned before, we did a couple of presentations with Microx. We did some presentations as well with people from the OpenTelemetry community as well. that basically allows us to show different aspects of how workflows are executed or how workflows are tested. And for that reason, I wanted to share with you this repository that is called Diagrid Labs Workflow Patterns Spring Boot that contains a list of different workflow patterns that you can implement using the building blocks that I just described. And I want to show you two things. The first thing without going into too much details because we don't have time for that, is the fact that using a very similar setup to the one that I show you, you can add something like zip key to basically see all the traces of all the workflow executions. So you can get more visibility about like what happened, which services were called and how much time did it took to call all the services and how much time did the orchestration took, which I think it's pretty important if you're building like highly scalable systems. So what you can see here in the diagram is basically one of the traces of just running one of these examples. And you can see again, like how much time, like it took one millisecond to schedule that task. But then of course it took more time to execute the activity itself because there was a delay inside the activity, right? So you can quickly compare how much time does the orchestrator takes compared to executing activities and, you know, and waiting for results to come back from third party services. If you are building complex orchestrations inside these activities, you will be calling other services. So you want to make sure that, for example, that you do trace propagation whenever you call an external service. And the best way to check that that's happening is by looking into something like Zipkin that will give you visibility across all the traces and all the services that were called as part of an orchestration. Cool. That's one thing. The other thing that I wanted to mention is about testing workflows, because I think that... Again, the more complex the orchestration is, the more complex it's going to be to test that. And I can go back to my IDE here because it's something that I didn't show you. But for example, if I stop the application now, what I can do is I can do a clean install or package any goal that will trigger the tests. And as part of the test, of course, I want to include a test to validate the execution of my workforce. So now I can reuse the same configuration that I use for running the application in local development mode, as I just showed you, but for writing integration tests in this case, right? So what I have here is like you can see, I run two tests, basically mimicking the same things that I did by sending these two requests, right? Like with different payloads. But now I have like, you know, integration tests that will test my application. and it will use the Dapper APIs to validate that the workflows were executed. So again, what you can see here is a Spring Boot 4 style test that will be sending some requests to the application after the application is started. It will validate that we get quickly an instance ID back from the endpoint, and then it will use the Dapper workflow client to check that that instance got completed, but also that the output of the workflow was the output that we expected. The same for the other use case where we send like a hello text there, we just need to expect a different output. So by doing this, you not only can quickly create workflows and activities and run the orchestrations in local development mode without installing the application or installing Dapper into a cluster, but also you can write very complex tests that will validate that your workflows are doing what they are supposed to be doing. But more importantly, you can validate that actually the services that needs to be called as part of your workflow orchestrations are being called. And that's where mocking services become kind of like really interesting and important. because you will be doing a lot of service to service interactions inside these orchestrations. And I think that that becomes kind of like fundamental to have like a complete picture on how to create that. And that's what we have here in this repository that I was showing you. So we have one of the patterns that it's called, it's called simple HTTP. Let me see simple HTTP endpoint request. Let me see if that's the one. Yeah, I think that's the one. Let's check. So if you take a look into the simple HTTP, so if you take a look into this example, in the source code of this example, what you will see is that inside of an activity, we are calling an HTTP endpoint and we want to make sure if that call fails, we can retry. And in order to do that, we are using this tool that's called microx. And I can show you here in the pom.xml file that we are adding this testing library. By adding this testing library, what we can do is we can configure Dapper to actually call a mock service instead of the real service, but just changing some parameters and some configurations in the source code of the test configuration that I showed you before. So we can go here and check a little bit on the test configurations. I want to show you quickly how that looks like for simple HTTP, no, here. So this looks pretty much the same as I showed you before. Of course, I've mentioned before, like that we're using zip key here for observability and seeing the traces and we're using Kafka instead of RabbitMQ to connect to different services. But the important bit here is this one, it's the microx containers and symbol that basically allows me to define, you know, using open API remote endpoints that I want to mock. So in this case we have an activity that is going to call a remote HTTP service and this container is going to be in charge of mocking those APIs for the user and for testing purposes. We have some other configurations that basically it's going to allow DAPR to connect to the services by using the service to service invocation endpoints and you can see how that's been configured here in this case like well there actually there is no configuration needed to do that. So you can see here that we're just configuring PubSub. Yeah, we're just configuring PubSub and we're configuring observability here as well. So no changes needed. You just need to create an activity and the microcs will pick up that request whenever we are running in test mode, right? Again, this is just for local development or writing tests against these services that are going to be mocked. Again check this repository it's Diagrid Labs workflow patterns Springwood that it contains all these examples that you can run on your own and change and test if you're already building workflows with upper workflows. Finally I wanted to mention something that I think it's important to mention related to Spring AI right because AI is really all over the place now and Spring is doing a very good job at creating examples. and creating all the frameworks that we need in order to interact with LLMs. I wrote a blog post about this some time ago, like last year, that it's hosted in this zone that is called Long-Running Durable Agents with Spring AI and Dapper Workflows. And this is just to show like another use that you can, another thing that you can do with Dapper Workflows that may be very useful if you're already using Spring AI. In this blog post where I discuss does is about like agentic patterns in general like the Spring AI folks they created. They created here something that is called Spring AI examples in their Spring project repository and inside that what they have is they have this directory that's called agentic patterns and this is as you can see everything is a workflow right like it's like chain workflow, parallelization workflow, routing workflow. orchestrator workers is a workflow and evaluator optimization is also a workflow but they chose different names for the just to continue a little bit about on that topic on how do we use workflows to make sure that these kind of applications are also durable I wanted to show you how this example look like because if you look at the code what you will see is that basically what they are trying to implement here is a very simple example that takes prompts. Let me see here. Let me go to the other class. I think it might be easier to explain with this. So basically what they do is they want to run different prompts in parallel, instead of like calling, doing a prompt with an item, then waiting for that to finish, and then just doing the same, almost the same prompt with some changes and just waiting for the response. And this is quite common. Look at the prompt that they have. So this is the generic part of the prompt that it's analyzed how market change will impact these stakeholder groups, provide specific impacts and recommended actions, format with clear sections and priorities. That's kind of like the generic part of the prompt, but then what they want to do is they want to run in parallel the same prompt, but for different stakeholder groups like customers, employers, investors, and suppliers. And what you can do here is you can actually say, okay, well, I have four different groups, so I want to parallelize with a thread pool of four, right? So now we have four threads and I can send each of these prompts to different threads so they run in parallel. And then I will need to wait for all of them to finish to get all the results together. And that's what it's implemented in this parallelization workflow class. So you can see they're using the chat builder here that basically provide access to sending prompts to different LLM models depending on the provider that you configure in your application. You need to provide also a token to interact with OpenAI or with the local model or with, you know, with Anthropic. So let's see how that implementation looks like here in the Spring AI examples. That implementation is very trivial because it takes the inputs which is like the generic prompt plus the array of inputs in this case is list of four inputs and what it does it will use the executors new fixed thread pool to create workers when we have four workers in this case what we can do is we can iterate the list of inputs and for each input we can basically send the prompt to the llm right but this is happening in parallel and because this is happening in parallel we need to basically wait for all the completable features to finish in order to get all the results and that's why we're using join in this line right so basically what this is what this is happening is is i'm starting the application i'm sending all the prompts in four different threads and then i'm waiting for all of them to finish with this join and then collecting all the results and returning that as a list to the to the to the user right and again you can build the spring with application very simple way nowadays you can copy the examples but what i did for my example application is to uh spring ai you can see here the dependency so i added this one spring ai uh from open ai so i'm just interacting with open ai so i need an open ai token and uh pretty much nothing else right i just created the parallelization workflow which is the same class that i just showed you that used the executor rule to send these requests. And I added some endpoints here to my REST controller. And of course I'm auto wiring the chat client builder as well so I can send prompts. So you see here how the same prompts that I showed you before. And I have an endpoint that it's called parallel, right? That it's going to basically run that for me. So maybe we can do that. This might fail if I do not have... if I do not have the token set up correctly. So let's see, it should work. All right, so this is starting the application. It's starting Dapper and Dapper Workflows. Now for running this example, we are not using Workflows. We are just using the in-memory executor pools. And I guess that's like, you can see that that might be the problem with that. So what I can do now is I can send a request, HTTP 8080 parallel. I need to send the post request, of course. I need to send the post request. And of course, now I'm sending, you know, a prompt to the LLM. Let's see here. Let me see if I can make this screen a little bit smaller. I'm sending a prompt to the LLM. That's a warning. That's not the problem. And at some point I should be able to get the results, right? Like, so it's processing the prompts. Let's see if that comes back. At some point it should come back. I don't know if I clean it just. at the wrong time. Let me call that again. Let's see what happens. Yeah, so you can see that this is basically now waiting for all the prompts to return. If OpenAI is working as expected, we should be able to see the output at some point here, right? Because we're going to send the list of results back. But the problem with this approach, and this might take a while for some reason. All right, no. So there is something broken there, but we can look into that later. The thing that I wanted to explain here is that all these things happen in memory. Okay, now there you go. Then we have the answers, right? Okay, so that's kind of like the output from the four prompts that were sent, right? So we have an explanation per user, per stakeholder group, right? Which is pretty long, as you can see here. Yeah, overview, so that's good. And we should have like four entries, I think, in that because it's a list of strings. Yeah, so it's an array. So, okay, we have that. But the problem with this is that we are running four Parallel prompts. And again, if the application crashes at any time, when we restart the application, we are going to execute the prompts again in this case, right? Or we will need to wait for a new request to actually finish processing that. And again, this is one of the classical scenarios where Dapper workflows can help you because what we can do here is we just can implement the same logic, exactly the same logic that we have here. But instead of using executors, we use the Dapper workflow engine to deal with the parallelism of all these tasks. So that's why I created this called parallel workflow that does exactly the same logic, right? Like it starts a new instance, we get the input, which is basically the generic prompt plus the list of inputs. And then for each input, we basically do a call activity. And again, this makes the activity durable. And if we look into the activity, the activity is doing pretty much the same thing that the other code was doing, which is basically sending a prompt. But now because it's wrapped inside an activity, if we already send this prompt and we got the result and the application crashes, we don't really need to execute this prompt again because we already have the results. So this makes it durable and it will implement the same logic, right? Like we are calling four things in parallel. You can see here that we are doing call activity. We are not awaiting for it to finish. We're just scheduling four at the same time and then waiting for all these activities to be scheduled back by the engine. But in the same way that we did with the executor pool, we need to wait for all of them to complete so we can actually get a list of responses. Right. And then we complete the workflow with that. So same logic, but now it's durable. We are encapsulating each prompt into a durable activity and imagine a scenarios where not all the things are in parallel, but when we do like. four in parallel then we do one that is synchronous that we need to wait for it and then do more in parallel. No more fails, we can just resume from where it left off and that will save us so many tokens that I totally see the benefits of using a solution like this. With that I think that I'm just finishing off this session for today. If you have any questions about this, or if you want to actually run this kind of dapper workflows in production, I strongly recommend you to check Diagrid Catalyst that it's, you know, on a platform that is built for running this at scale. It provides a lot of the managing tools that you will need to scale up solutions like this, plus, you know, UIs that will allow you to understand the architecture of your application. And whenever an issue pops up, you will find there. and all the metrics and all the things that you need to understand where the bottlenecks are so you can actually improve your application performance and build larger applications as well. Of course, you have the Workflow Visualizer side of things built in, and we are working to add more and more integrations on the Spring Boot ecosystem to the UI as well. So like other things that are happening now, that's it, that's it. So that's what's going on. okay so i don't think that i wanted to mention a couple of other things that are happening in the javaspace because you will see this being announced at some point uh the the spring ai integration is there we are building more integrations with workflows i've shared the link for that this on blog post as well but also launching 4j is happening at the same time integrated with quarkus as well so if you are a quarkus user you should be able to check that and we keep integrating you know other Dapper APIs to the Spring Boot ecosystem, like using the scheduler annotation to register asynchronous jobs, right? Just to make sure that Spring Boot developers can keep using their framework as they are used to, but under the hood, we are wiring up with Dapper services so you can get the best of these two worlds. So hopefully you enjoyed this session and if you have more questions, feel free to reach out. See you out there in the community. Thank you so much.

⚙️ Pipeline jobs

StageStatusAtt.UpdatedError
download done 1/3 2026-07-20 15:04:31
transcribe done 1/3 2026-07-20 15:05:02
summarize done 1/3 2026-07-20 15:05:29
embed done 1/3 2026-07-20 15:05:31

📄 Описание YouTube

Показать
In this workshop, we walk through how to create Dapr Workflows from Spring Boot 4 applications, including different workflow patterns and how to use the Testcontainers integration to monitor workflow executions using Diagrid’s Dapr Workflows Dashboard. You’ll also get practical insight into creating and running workflows that fully integrate with the Spring Boot ecosystem.

Who this workshop is for:
• Java developers
• Spring Boot developers
• Java architects