Slide 1

Slide 1 text

Beyond REST and CRUD Integration patterns in Microservices ➕ Erin Schnabel, Red Hat ・ ・ Zineb Bendhiba, Red Hat ・ ・ github.com/ebullient www.ebullient.dev github.com/zbendhiba zinebbendhiba.com 1

Slide 2

Slide 2 text

Essential factors Autonomous Portable Disposable 2

Slide 3

Slide 3 text

Essential factors Autonomous Portable Disposable Independent 2

Slide 4

Slide 4 text

How do we adapt? Applications must evolve 3

Slide 5

Slide 5 text

How do we adapt? Applications must evolve Rarely ever a clean slate 3

Slide 6

Slide 6 text

How do we adapt? Applications must evolve Rarely ever a clean slate Decouple elements of the system 3

Slide 7

Slide 7 text

Systems Integration Different perspective 4

Slide 8

Slide 8 text

Systems Integration Different perspective Message Routing 4

Slide 9

Slide 9 text

Enterprise patterns Emergent "Design patterns" Best practices 5

Slide 10

Slide 10 text

Apache Camel Integration framework Anything to anything Fit for purpose: ... use what you need 6

Slide 11

Slide 11 text

Concise Application Messaging Exchange Language 7

Slide 12

Slide 12 text

Term Meaning Message data transferred by a route Exchange envelope; wraps the data Endpoint a channel; receiver or sender Component know-how; creates endpoints Processor Java API; custom logic Camel message routing... 8

Slide 13

Slide 13 text

Pockets Inventory management Command-line local-only Web UI 9

Slide 14

Slide 14 text

Pockets: The plan 10

Slide 15

Slide 15 text

Pockets: The plan 10

Slide 16

Slide 16 text

Pockets: The plan 10

Slide 17

Slide 17 text

Pockets: The plan 10

Slide 18

Slide 18 text

Pockets: The plan 10

Slide 19

Slide 19 text

Pockets: The plan 10

Slide 20

Slide 20 text

Pockets: The plan 10

Slide 21

Slide 21 text

Pockets: The plan 10

Slide 22

Slide 22 text

Pockets: The plan 10

Slide 23

Slide 23 text

REST and REST... @Path("/pockets") public class TheoreticalPockets { @GET public Response getProfiles() { // fetch some stuff } @GET @Path("{profile}") public Response getProfile(@PathParam("profile") String id) { // fetch specific stuff } @POST @Path("{profile}") public Response updateProfile(@PathParam("profile") String id, ...) { // do important things } } 11

Slide 24

Slide 24 text

REST and REST... REST component adds support for REST semantics, including a DSL. 12

Slide 25

Slide 25 text

Flow of data with Camel Route: REST rest("/pockets") .post("/{profile}") .to("direct:validateAndModifyPockets"); // <--- HERE 1 .get() 2 .to("direct:getProfiles") 3 4 .get("/{profile}") 5 .to("direct:getProfile") 6 7 8 9 13

Slide 26

Slide 26 text

Flow of data with Camel Route: REST 13

Slide 27

Slide 27 text

Flow of data with Camel Route: REST 13

Slide 28

Slide 28 text

Flow of data with Camel Route: REST 13

Slide 29

Slide 29 text

Flow of data with Camel Route: REST @Transactional public void validateProfile(Exchange exchange) { Message message = exchange.getMessage(); String profile = (String) message.getHeader(profileKey); profile = ProfileContext.validateProfile(profile); message.setHeader(profileKey, profile); 1 2 final String profileKey = "profile"; 3 4 5 6 7 // Update header with full name, may throw 8 9 10 11 } 12 13

Slide 30

Slide 30 text

Flow of data with Camel Route: REST 13

Slide 31

Slide 31 text

Flow of data with Camel Route: REST .bean(webRouteHandler, "doModification(`${body}, $`{header.profile})") @Transactional public ModificationResponse doModification( ModificationRequest modificationRequest, String profileName) { profileContext.setActiveProfile(profileName); return modifyPockets.modifyPockets(modificationRequest); 1 2 ~~~~~~ 3 4 5 6 7 8 9 10 11 12 } 13 13

Slide 32

Slide 32 text

Flow of data with Camel Route: REST 13

Slide 33

Slide 33 text

Flow of data with Camel Route: CLI @ActivateRequestContext @Transactional String activeProfile = activeProfileMixin.findActiveProfileId(); profileContext.setActiveProfile(activeProfile); return delegateCall(); // delegate to sub-command @Override 1 2 3 public final Integer call() { 4 try { 5 // Validate specified profile 6 7 8 if (activeProfile == null) { 9 return ExitCode.USAGE; 10 } 11 12 13 if (showTypes) { 14 showTypes(); 15 return ExitCode.OK; 16 } 17 18 19 } catch (...) { 20 ... 21 } 22 } 23 14

Slide 34

Slide 34 text

Flow of data with Camel Route: CLI public Integer delegateCall() { Modification modification = createModification(); ModificationRequest req = createModificationRequest( "Create " + modification.itemDetails.name) ModificationResponse response = modifyPocketRoute.modifyPockets(req); @Override 1 2 3 4 5 6 7 .add(modification); 8 9 // Make the modification 10 11 12 response.changes.stream().filter(c -> c.type == PostingType.CREATE) 13 .forEach(c -> { 14 ... 15 }); 16 17 return ExitCode.OK; 18 } 19 15

Slide 35

Slide 35 text

Flow of data with Camel Route: CLI // Make the modification ModificationResponse response = modifyPocketRoute.modifyPockets(req); public interface CliModifyPocket { ModificationResponse modifyPockets(ModificationRequest request); } @Produce("direct:modifyPockets") CliModifyPocket modifyPocketRoute; 1 2 3 ~~~~ 4 5 6 7 8 9 10 11 16

Slide 36

Slide 36 text

from("direct:modifyPockets") .to("log:dev.ebullient.pockets?level=DEBUG") .bean(modifyPockets, "modifyPockets(${body})") .process((e) -> emitHandler.setProfile(e)) .wireTap("direct:emitRoute") .to("log:dev.ebullient.pockets?level=DEBUG"); 1 .errorHandler(noErrorHandler()) 2 3 4 5 6 7 8 9 10 11 12 Flow of data with Camel Route: CLI 17

Slide 37

Slide 37 text

from("direct:modifyPockets") .to("log:dev.ebullient.pockets?level=DEBUG") .bean(modifyPockets, "modifyPockets(${body})") .process((e) -> emitHandler.setProfile(e)) .wireTap("direct:emitRoute") .to("log:dev.ebullient.pockets?level=DEBUG"); 1 .errorHandler(noErrorHandler()) 2 3 4 5 6 7 8 9 10 11 12 Flow of data with Camel Route: CLI 17

Slide 38

Slide 38 text

from("direct:modifyPockets") .to("log:dev.ebullient.pockets?level=DEBUG") .bean(modifyPockets, "modifyPockets(${body})") .process((e) -> emitHandler.setProfile(e)) .wireTap("direct:emitRoute") .to("log:dev.ebullient.pockets?level=DEBUG"); 1 .errorHandler(noErrorHandler()) 2 3 4 5 6 7 8 9 10 11 12 Flow of data with Camel Route: CLI 17

Slide 39

Slide 39 text

from("direct:modifyPockets") .to("log:dev.ebullient.pockets?level=DEBUG") .bean(modifyPockets, "modifyPockets(${body})") .process((e) -> emitHandler.setProfile(e)) .wireTap("direct:emitRoute") .to("log:dev.ebullient.pockets?level=DEBUG"); 1 .errorHandler(noErrorHandler()) 2 3 4 5 6 7 8 9 10 11 12 Flow of data with Camel Route: CLI 17

Slide 40

Slide 40 text

from("direct:modifyPockets") .to("log:dev.ebullient.pockets?level=DEBUG") .bean(modifyPockets, "modifyPockets(${body})") .process((e) -> emitHandler.setProfile(e)) .wireTap("direct:emitRoute") .to("log:dev.ebullient.pockets?level=DEBUG"); 1 .errorHandler(noErrorHandler()) 2 3 4 5 6 7 8 9 10 11 12 Flow of data with Camel Route: CLI public void setProfile(Exchange e) { ModificationResponse resp = msg.getBody(ModificationResponse.class); msg.setHeader("profile", resp.profileName); 1 Message msg = e.getMessage(); 2 3 4 5 6 } 7 17

Slide 41

Slide 41 text

from("direct:modifyPockets") .to("log:dev.ebullient.pockets?level=DEBUG") .bean(modifyPockets, "modifyPockets(${body})") .process((e) -> emitHandler.setProfile(e)) .wireTap("direct:emitRoute") .to("log:dev.ebullient.pockets?level=DEBUG"); 1 .errorHandler(noErrorHandler()) 2 3 4 5 6 7 8 9 10 11 12 Flow of data with Camel Route: CLI 17

Slide 42

Slide 42 text

Common path: WireTap What does this do? .wireTap("direct:emitRoute") 18

Slide 43

Slide 43 text

Common path: WireTap What does this do? .wireTap("direct:emitRoute") It kicks off a new route to transform content 18

Slide 44

Slide 44 text

Common path: WireTap What does this do? .wireTap("direct:emitRoute") It kicks off a new route to transform content from("direct:emitRoute") .bean(emitHandler, "emitRoute") 1 .to("log:dev.ebullient.pockets?level=DEBUG") 2 3 .recipientList(header("routes")) 4 .parallelProcessing(); 5 18

Slide 45

Slide 45 text

Common path: Recipient List public void emitRoute(Exchange exchange) { String profileName = msg.getHeader("profile", "default", String.class); List emitters = new ArrayList<>(); if (localPocketsConfig.jsonEventLogEnabled(profileName)) { emitters.add("direct:jsonEmitter"); if (localPocketsConfig.markdownEnabled(profileName)) { emitters.add("direct:markdownEmitter"); msg.setHeader("routes", String.join(",", emitters)); @SuppressWarnings("unused") 1 2 Message msg = exchange.getMessage(); 3 4 5 6 7 EventLogConfiguration eventLogConfig = 8 localPocketsConfig.eventLogConfig(profileName); 9 msg.setHeader("jsonEventLogDirectory", 10 eventLogConfig.getDirectory().toString()); 11 12 } 13 14 15 } 16 17 } 18 19

Slide 46

Slide 46 text

Common path: Recipient List .recipientList(header("routes")) .parallelProcessing(); from("direct:emitRoute") 1 .to("log:dev.ebullient.pockets?level=DEBUG") 2 .bean(emitHandler, "emitRoute") 3 4 5 20

Slide 47

Slide 47 text

Common path: direct:jsonEmitter from("direct:jsonEmitter") .setHeader(FileConstants.FILE_NAME, simple("${header.profile}.events.jsonl")) .setBody((e) -> Transform.toJsonString(e.getMessage().getBody())) .toD("file:${header.jsonEventLogDirectory}?" + "fileExist=Append&appendChars=\n&allowNullBody=true") 1 .onException(InvalidPocketState.class) 2 .handled(true) 3 .process(new HandleInvalidPocketState()) 4 .end() 5 6 7 8 9 10 11 12 13 14 .to("log:dev.ebullient.pockets?level=DEBUG"); 15 21

Slide 48

Slide 48 text

Common path: Markdown emitter 22

Slide 49

Slide 49 text

Common path: Qute templates from("direct:markdownFile") .to("qute:dummy?allowTemplateFromHeader=true") .toD("file:${header.markdownDirectory}?" + "fileExist=Override&allowNullBody=true") 1 .onException(InvalidPocketState.class) 2 .handled(true) 3 .process(new EmitRouteHandler.HandleInvalidPocketState()) 4 .end() 5 6 7 8 9 10 11 .to("log:dev.ebullient.pockets?level=DEBUG"); 12 23

Slide 50

Slide 50 text

➡ 24

Slide 51

Slide 51 text

Summary Apache Camel can help fill in the gaps Use it alongside your favorite messaging solution (Kafka or traditional message brokers) 25

Slide 52

Slide 52 text

Summary Apache Camel can help fill in the gaps Use it alongside your favorite messaging solution (Kafka or traditional message brokers) It applies to patterns far beyond REST and CRUD 25

Slide 53

Slide 53 text

Summary Apache Camel can help fill in the gaps Use it alongside your favorite messaging solution (Kafka or traditional message brokers) It applies to patterns far beyond REST and CRUD Use it with your favorite Java framework 25

Slide 54

Slide 54 text

Summary Apache Camel can help fill in the gaps Use it alongside your favorite messaging solution (Kafka or traditional message brokers) It applies to patterns far beyond REST and CRUD Use it with your favorite Java framework Use it with JBang for script nonsense 25

Slide 55

Slide 55 text

Summary Apache Camel can help fill in the gaps Use it alongside your favorite messaging solution (Kafka or traditional message brokers) It applies to patterns far beyond REST and CRUD Use it with your favorite Java framework Use it with JBang for script nonsense Use it with Kubernetes to make the best glue (Camel-K, Kamelets) 25

Slide 56

Slide 56 text

➕ ~fin~ 26

Slide 57

Slide 57 text

No content