Event driven microservices tutorial (#15646)

* even driven microservices tutorial

* restructure the project

* remove mvn files

* formatting
This commit is contained in:
Viren Baraiya 2024-01-25 19:24:18 -08:00 committed by GitHub
parent 25c40ac1ca
commit dcb4eecaf5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 444 additions and 0 deletions

View File

@ -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/

View File

@ -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
```

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.orkes.demo</groupId>
<artifactId>event-driven-microservice</artifactId>
<version>0.1</version>
<name>event-driven-microservice</name>
<description>Demo Project for Orkes Conductor on Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.orkes.conductor</groupId>
<artifactId>orkes-conductor-client</artifactId>
<version>${conductor.client.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${spring.webmvc.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<java.version>17</java.version>
<conductor.client.version>2.0.8</conductor.client.version>
<spring.webmvc.version>2.1.0</spring.webmvc.version>
</properties>
</project>

View File

@ -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();
}
}

View File

@ -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<FraudCheckResult> 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<String, Object> checkForFraud(@RequestBody DepositDetail depositDetail) throws Exception {
log.info("Checking if fraud check is required for: {}", depositDetail);
return workflowService.executeWorkflow(depositDetail);
}
// docs-marker-end-1
}

View File

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

View File

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

View File

@ -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<String, Object> startDepositWorkflow(DepositDetail depositDetail) {
StartWorkflowRequest request = new StartWorkflowRequest();
request.setName("microservice_orchestration");
Map<String, Object> 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<String, Object> executeWorkflow(DepositDetail depositDetail) throws ExecutionException, InterruptedException, TimeoutException {
StartWorkflowRequest request = new StartWorkflowRequest();
request.setName("microservice_orchestration");
request.setVersion(1);
Map<String, Object> inputData = new HashMap<>();
inputData.put("amount", depositDetail.getAmount());
inputData.put("accountId", depositDetail.getAccountId());
request.setInput(inputData);
CompletableFuture<WorkflowRun> workflowRun = workflowClient.executeWorkflow(request, UUID.randomUUID()
.toString(), 10);
log.info("Workflow id: {}", workflowRun);
return workflowRun.get(10, TimeUnit.SECONDS)
.getOutput();
}
}

View File

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

View File

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

View File

@ -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/

View File

@ -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() {
}
}

View File

@ -0,0 +1,65 @@
<code_scheme name="baeldung-formatter" version="173">
<option name="RIGHT_MARGIN" value="260"/>
<option name="FORMATTER_TAGS_ENABLED" value="true"/>
<JavaCodeStyleSettings>
<option name="SPACE_AFTER_CLOSING_ANGLE_BRACKET_IN_TYPE_ARGUMENT" value="true"/>
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="100"/>
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="100"/>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="" withSubpackages="true" static="true"/>
<emptyLine/>
<package name="java" withSubpackages="true" static="false"/>
<emptyLine/>
<package name="javax" withSubpackages="true" static="false"/>
<emptyLine/>
<package name="org" withSubpackages="true" static="false"/>
<emptyLine/>
<package name="com" withSubpackages="true" static="false"/>
<emptyLine/>
<package name="" withSubpackages="true" static="false"/>
</value>
</option>
<option name="ENABLE_JAVADOC_FORMATTING" value="false"/>
</JavaCodeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="160"/>
<option name="KEEP_LINE_BREAKS" value="false"/>
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false"/>
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false"/>
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/>
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/>
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/>
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1"/>
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/>
<option name="ALIGN_MULTILINE_THROWS_LIST" value="true"/>
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true"/>
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true"/>
<option name="CALL_PARAMETERS_WRAP" value="1"/>
<option name="METHOD_PARAMETERS_WRAP" value="1"/>
<option name="RESOURCE_LIST_WRAP" value="5"/>
<option name="EXTENDS_LIST_WRAP" value="1"/>
<option name="THROWS_LIST_WRAP" value="1"/>
<option name="EXTENDS_KEYWORD_WRAP" value="1"/>
<option name="THROWS_KEYWORD_WRAP" value="1"/>
<option name="METHOD_CALL_CHAIN_WRAP" value="2"/>
<option name="BINARY_OPERATION_WRAP" value="1"/>
<option name="TERNARY_OPERATION_WRAP" value="1"/>
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true"/>
<option name="ARRAY_INITIALIZER_WRAP" value="1"/>
<option name="IF_BRACE_FORCE" value="3"/>
<option name="DOWHILE_BRACE_FORCE" value="3"/>
<option name="WHILE_BRACE_FORCE" value="3"/>
<option name="FOR_BRACE_FORCE" value="3"/>
<option name="ENUM_CONSTANTS_WRAP" value="2"/>
<option name="KEEP_BUILDER_METHODS_INDENTS" value="true"/>
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4"/>
<option name="SMART_TABS" value="true"/>
</indentOptions>
<arrangement>
<groups/>
<rules/>
</arrangement>
</codeStyleSettings>
</code_scheme>

View File

@ -22,6 +22,7 @@
<module>msf4j</module>
<module>open-liberty</module>
<module>rest-express</module>
<module>event-driven-microservice</module>
</modules>
</project>