From dcb4eecaf51e1aa95e3ecf217035aa46925ee17b Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Thu, 25 Jan 2024 19:24:18 -0800 Subject: [PATCH] Event driven microservices tutorial (#15646) * even driven microservices tutorial * restructure the project * remove mvn files * formatting --- .../event-driven-microservice/.gitignore | 27 +++++++ .../event-driven-microservice/README.md | 13 ++++ .../event-driven-microservice/pom.xml | 76 +++++++++++++++++++ .../io/orkes/demo/banking/Application.java | 28 +++++++ .../banking/controller/APIController.java | 41 ++++++++++ .../demo/banking/pojos/DepositDetail.java | 19 +++++ .../banking/service/FraudCheckService.java | 29 +++++++ .../demo/banking/service/WorkflowService.java | 71 +++++++++++++++++ .../banking/workers/ConductorWorkers.java | 34 +++++++++ .../banking/workers/FraudCheckResult.java | 15 ++++ .../src/main/resources/application.properties | 11 +++ .../orkes/demo/banking/ApplicationTests.java | 14 ++++ .../event-driven-microservice/style.xml | 65 ++++++++++++++++ microservices-modules/pom.xml | 1 + 14 files changed, 444 insertions(+) create mode 100644 microservices-modules/event-driven-microservice/.gitignore create mode 100644 microservices-modules/event-driven-microservice/README.md create mode 100644 microservices-modules/event-driven-microservice/pom.xml create mode 100644 microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/Application.java create mode 100644 microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/controller/APIController.java create mode 100644 microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/pojos/DepositDetail.java create mode 100644 microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/service/FraudCheckService.java create mode 100644 microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/service/WorkflowService.java create mode 100644 microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/workers/ConductorWorkers.java create mode 100644 microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/workers/FraudCheckResult.java create mode 100644 microservices-modules/event-driven-microservice/src/main/resources/application.properties create mode 100644 microservices-modules/event-driven-microservice/src/test/java/io/orkes/demo/banking/ApplicationTests.java create mode 100644 microservices-modules/event-driven-microservice/style.xml diff --git a/microservices-modules/event-driven-microservice/.gitignore b/microservices-modules/event-driven-microservice/.gitignore new file mode 100644 index 0000000000..98c0d387a6 --- /dev/null +++ b/microservices-modules/event-driven-microservice/.gitignore @@ -0,0 +1,27 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +.idea/ +target/ diff --git a/microservices-modules/event-driven-microservice/README.md b/microservices-modules/event-driven-microservice/README.md new file mode 100644 index 0000000000..657f2ec6b7 --- /dev/null +++ b/microservices-modules/event-driven-microservice/README.md @@ -0,0 +1,13 @@ +# Event Driven Microservices using Conductor + +This is an example project showing how to build event driven applications using [Conductor](https://github.com/conductor-oss/conductor) + +# Pre-requisites +1. Docker +2. Running conductor server + +**Start the conductor server** + +```shell +docker run --init -p 8080:8080 -p 1234:5000 conductoross/conductor-standalone:3.15.0 +``` \ No newline at end of file diff --git a/microservices-modules/event-driven-microservice/pom.xml b/microservices-modules/event-driven-microservice/pom.xml new file mode 100644 index 0000000000..a1ba8d6e35 --- /dev/null +++ b/microservices-modules/event-driven-microservice/pom.xml @@ -0,0 +1,76 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.2.0 + + + + io.orkes.demo + event-driven-microservice + 0.1 + + event-driven-microservice + Demo Project for Orkes Conductor on Spring Boot + + + + + + org.springframework.boot + spring-boot-starter-web + + + + io.orkes.conductor + orkes-conductor-client + ${conductor.client.version} + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${spring.webmvc.version} + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + + 17 + 2.0.8 + 2.1.0 + + + diff --git a/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/Application.java b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/Application.java new file mode 100644 index 0000000000..441f85f23c --- /dev/null +++ b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/Application.java @@ -0,0 +1,28 @@ +package io.orkes.demo.banking; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.netflix.conductor.common.config.ObjectMapperProvider; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +@SpringBootApplication +@ComponentScan(basePackages = { "io.orkes" }) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + // ObjectMapper instance used for JSON serialization - can be modified to configure additional modules + @Bean + public ObjectMapper getObjectMapper() { + return new ObjectMapperProvider().getObjectMapper(); + } + +} diff --git a/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/controller/APIController.java b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/controller/APIController.java new file mode 100644 index 0000000000..6b89bdddae --- /dev/null +++ b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/controller/APIController.java @@ -0,0 +1,41 @@ +package io.orkes.demo.banking.controller; + +import java.util.Map; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import io.orkes.demo.banking.pojos.DepositDetail; +import io.orkes.demo.banking.service.FraudCheckService; +import io.orkes.demo.banking.service.WorkflowService; +import io.orkes.demo.banking.workers.FraudCheckResult; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@AllArgsConstructor +@RestController +public class APIController { + + private final FraudCheckService fraudCheckService; + + private final WorkflowService workflowService; + + @PostMapping(value = "/triggerDeposit", produces = "application/json") + public ResponseEntity triggerDeposit(@RequestBody DepositDetail depositDetail) { + log.info("Checking for fraud: {}", depositDetail); + return ResponseEntity.ok(fraudCheckService.checkForFraud(depositDetail)); + } + + // docs-marker-start-1 + @PostMapping(value = "/checkForFraud", produces = "application/json") + public Map checkForFraud(@RequestBody DepositDetail depositDetail) throws Exception { + log.info("Checking if fraud check is required for: {}", depositDetail); + return workflowService.executeWorkflow(depositDetail); + } + + // docs-marker-end-1 + +} diff --git a/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/pojos/DepositDetail.java b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/pojos/DepositDetail.java new file mode 100644 index 0000000000..7c382398eb --- /dev/null +++ b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/pojos/DepositDetail.java @@ -0,0 +1,19 @@ +package io.orkes.demo.banking.pojos; + +import java.math.BigDecimal; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DepositDetail { + + private String accountId; + private BigDecimal amount; + +} diff --git a/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/service/FraudCheckService.java b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/service/FraudCheckService.java new file mode 100644 index 0000000000..80bd8b8886 --- /dev/null +++ b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/service/FraudCheckService.java @@ -0,0 +1,29 @@ +package io.orkes.demo.banking.service; + +import static io.orkes.demo.banking.workers.FraudCheckResult.Result.FAIL; +import static io.orkes.demo.banking.workers.FraudCheckResult.Result.PASS; + +import java.math.BigDecimal; + +import org.springframework.stereotype.Service; + +import io.orkes.demo.banking.pojos.DepositDetail; +import io.orkes.demo.banking.workers.FraudCheckResult; + +@Service +public class FraudCheckService { + + public FraudCheckResult checkForFraud(DepositDetail depositDetail) { + FraudCheckResult fcr = new FraudCheckResult(); + if (depositDetail.getAmount() + .compareTo(BigDecimal.valueOf(100000)) > 0) { + fcr.setResult(FAIL); + fcr.setReason("Amount too large"); + } else { + fcr.setResult(PASS); + fcr.setReason("All good!"); + } + return fcr; + } + +} diff --git a/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/service/WorkflowService.java b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/service/WorkflowService.java new file mode 100644 index 0000000000..843508d190 --- /dev/null +++ b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/service/WorkflowService.java @@ -0,0 +1,71 @@ +package io.orkes.demo.banking.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.springframework.stereotype.Service; + +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; + +import io.orkes.conductor.client.WorkflowClient; +import io.orkes.conductor.common.model.WorkflowRun; +import io.orkes.demo.banking.pojos.DepositDetail; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@AllArgsConstructor +@Service +public class WorkflowService { + + private final WorkflowClient workflowClient; + + /** + * Starts the workflow execution asynchronously + * @param depositDetail + * @return + */ + public Map startDepositWorkflow(DepositDetail depositDetail) { + StartWorkflowRequest request = new StartWorkflowRequest(); + request.setName("microservice_orchestration"); + Map inputData = new HashMap<>(); + inputData.put("amount", depositDetail.getAmount()); + inputData.put("accountId", depositDetail.getAccountId()); + request.setInput(inputData); + + String workflowId = workflowClient.startWorkflow(request); + log.info("Workflow id: {}", workflowId); + return Map.of("workflowId", workflowId); + } + + /** + * Executes the workflow, waits for it to complete and returns the output of the workflow + * @param depositDetail + * @return + * @throws ExecutionException + * @throws InterruptedException + * @throws TimeoutException + */ + public Map executeWorkflow(DepositDetail depositDetail) throws ExecutionException, InterruptedException, TimeoutException { + StartWorkflowRequest request = new StartWorkflowRequest(); + request.setName("microservice_orchestration"); + request.setVersion(1); + Map inputData = new HashMap<>(); + inputData.put("amount", depositDetail.getAmount()); + inputData.put("accountId", depositDetail.getAccountId()); + request.setInput(inputData); + + CompletableFuture workflowRun = workflowClient.executeWorkflow(request, UUID.randomUUID() + .toString(), 10); + log.info("Workflow id: {}", workflowRun); + + return workflowRun.get(10, TimeUnit.SECONDS) + .getOutput(); + } + +} diff --git a/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/workers/ConductorWorkers.java b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/workers/ConductorWorkers.java new file mode 100644 index 0000000000..5f5256a3ca --- /dev/null +++ b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/workers/ConductorWorkers.java @@ -0,0 +1,34 @@ +package io.orkes.demo.banking.workers; + +import java.math.BigDecimal; + +import org.springframework.stereotype.Component; + +import com.netflix.conductor.sdk.workflow.task.InputParam; +import com.netflix.conductor.sdk.workflow.task.WorkerTask; + +import io.orkes.demo.banking.pojos.DepositDetail; +import io.orkes.demo.banking.service.FraudCheckService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@AllArgsConstructor +@Component +@Slf4j +public class ConductorWorkers { + + private final FraudCheckService fraudCheckService; + + /** + * + * @param amount + * @return Given the amount, the service check if the fraud check should done before executing the transaction + */ + @WorkerTask(value = "fraud-check-required") + public FraudCheckResult simpleWorker(@InputParam("amount") BigDecimal amount) { + DepositDetail dd = new DepositDetail(); + dd.setAmount(amount); + return fraudCheckService.checkForFraud(dd); + } + +} diff --git a/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/workers/FraudCheckResult.java b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/workers/FraudCheckResult.java new file mode 100644 index 0000000000..b4e667910f --- /dev/null +++ b/microservices-modules/event-driven-microservice/src/main/java/io/orkes/demo/banking/workers/FraudCheckResult.java @@ -0,0 +1,15 @@ +package io.orkes.demo.banking.workers; + +import lombok.Data; + +@Data +public class FraudCheckResult { + + public enum Result { + PASS, + FAIL; + } + + private Result result; + private String reason; +} diff --git a/microservices-modules/event-driven-microservice/src/main/resources/application.properties b/microservices-modules/event-driven-microservice/src/main/resources/application.properties new file mode 100644 index 0000000000..6f31f7f1f2 --- /dev/null +++ b/microservices-modules/event-driven-microservice/src/main/resources/application.properties @@ -0,0 +1,11 @@ +# swagger-ui custom path +springdoc.swagger-ui.path=/swagger-ui.html +management.endpoints.enabled-by-default=false +management.endpoint.info.enabled=false +server.port=8081 +# If you want to use Orkes Playground, then change the server url to https://play.orkes.io/api/ +# Obtain key and secret by logging into +# and navigating to applications menu, create an application and generate key/secret +conductor.security.client.key-id=CHANGE_ME +conductor.security.client.secret=CHANGE_ME +conductor.server.url=https://play.orkes.io/api/ \ No newline at end of file diff --git a/microservices-modules/event-driven-microservice/src/test/java/io/orkes/demo/banking/ApplicationTests.java b/microservices-modules/event-driven-microservice/src/test/java/io/orkes/demo/banking/ApplicationTests.java new file mode 100644 index 0000000000..9e5fe270b1 --- /dev/null +++ b/microservices-modules/event-driven-microservice/src/test/java/io/orkes/demo/banking/ApplicationTests.java @@ -0,0 +1,14 @@ +package io.orkes.demo.banking; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ApplicationTests { + + @Test + void contextLoads() { + + } + +} diff --git a/microservices-modules/event-driven-microservice/style.xml b/microservices-modules/event-driven-microservice/style.xml new file mode 100644 index 0000000000..e99b59d78e --- /dev/null +++ b/microservices-modules/event-driven-microservice/style.xml @@ -0,0 +1,65 @@ + + diff --git a/microservices-modules/pom.xml b/microservices-modules/pom.xml index a88b6e0fd6..0f0baac488 100644 --- a/microservices-modules/pom.xml +++ b/microservices-modules/pom.xml @@ -22,6 +22,7 @@ msf4j open-liberty rest-express + event-driven-microservice \ No newline at end of file