{"id":107682,"date":"2019-11-19T08:00:53","date_gmt":"2019-11-19T08:00:53","guid":{"rendered":"https:\/\/developers.redhat.com\/blog\/?p=648217"},"modified":"2019-11-19T08:00:53","modified_gmt":"2019-11-19T08:00:53","slug":"decoupling-microservices-with-apache-camel-and-debezium","status":"publish","type":"post","link":"https:\/\/sickgaming.net\/blog\/2019\/11\/19\/decoupling-microservices-with-apache-camel-and-debezium\/","title":{"rendered":"Decoupling microservices with Apache Camel and Debezium"},"content":{"rendered":"<p>The rise of <a href=\"https:\/\/developers.redhat.com\/topics\/microservices\/\">microservices<\/a>-oriented architecture brought us new development paradigms and mantras about independent development and decoupling. In such a scenario, we have to deal with a situation where we aim for independence, but we still need to react to state changes in different enterprise domains.<\/p>\n<p>I&#8217;ll use a simple and typical example in order to show what we&#8217;re talking about. Imagine the development of two independent microservices: <code>Order<\/code> and <code>User<\/code>. We designed them to expose a REST interface and to each use a separate database, as shown in Figure 1:<\/p>\n<div id=\"attachment_648347\" style=\"width: 628px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-648347\" class=\"wp-image-648347 size-full\" src=\"https:\/\/developers.redhat.com\/blog\/wp-content\/uploads\/2019\/11\/diagram-1.png\" alt=\"Diagram 1 - Order and User microservices\" width=\"618\" height=\"331\" srcset=\"https:\/\/developers.redhat.com\/blog\/wp-content\/uploads\/2019\/11\/diagram-1.png 618w, https:\/\/developers.redhat.com\/blog\/wp-content\/uploads\/2019\/11\/diagram-1-300x161.png 300w\" sizes=\"auto, (max-width: 618px) 100vw, 618px\" \/><\/p>\n<p id=\"caption-attachment-648347\" class=\"wp-caption-text\">Figure 1: Order and User microservices.<\/p>\n<\/div>\n<p><span id=\"more-648217\"><\/span><\/p>\n<p>We must notify the <code>User<\/code> domain about any change happening in the <code>Order<\/code> domain. To do this in the example, we need to update the <code>order_list<\/code>. For this reason, we&#8217;ve modeled the User REST service with\u00a0<code>addOrder<\/code> and\u00a0<code>deleteOrder<\/code> operations.<\/p>\n<h2>Solution 1: Queue decoupling<\/h2>\n<p>The first solution to consider is adding a queue between the services. <code>Order<\/code> will publish events that\u00a0<code>User<\/code> will eventually process, as shown in Figure 2:<\/p>\n<div id=\"attachment_648337\" style=\"width: 338px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-648337\" class=\"wp-image-648337 size-full\" src=\"https:\/\/developers.redhat.com\/blog\/wp-content\/uploads\/2019\/11\/diagram-2.png\" alt=\"Diagram 2 - decoupling with a queue\" width=\"328\" height=\"431\" srcset=\"https:\/\/developers.redhat.com\/blog\/wp-content\/uploads\/2019\/11\/diagram-2.png 328w, https:\/\/developers.redhat.com\/blog\/wp-content\/uploads\/2019\/11\/diagram-2-228x300.png 228w\" sizes=\"auto, (max-width: 328px) 100vw, 328px\" \/><\/p>\n<p id=\"caption-attachment-648337\" class=\"wp-caption-text\">Figure 2: Decoupling with a queue.<\/p>\n<\/div>\n<p>This is a fair design. However, if you don&#8217;t use the right middleware you will mix a lot of <em>infrastructure code<\/em> into your <em>domain logic. <\/em>Now that you have queues, you must develop <em>producer<\/em> and <em>consumer logic<\/em>. You also have to take care of <em>transactions. <\/em>The problem is to make sure that every event ends up correctly in both the <code>Order<\/code> database and in the queue.<\/p>\n<h2>Solution 2: Change data capture decoupling<\/h2>\n<p>Let me introduce an alternative solution that handles all of that work without your touching any line of your microservices code. I&#8217;ll use <a href=\"https:\/\/debezium.io\/\" target=\"_blank\" rel=\"noopener noreferrer\">Debezium<\/a> and <a href=\"https:\/\/camel.apache.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">Apache Camel<\/a>\u00a0to capture data changes on <code>Order<\/code> and trigger certain actions on <code>User<\/code>. Debezium is a log-based data change capture middleware. Camel is an integration framework that simplifies the integration between a source (<code>Order<\/code>) and a destination (<code>User<\/code>), as shown in Figure 3:<\/p>\n<div id=\"attachment_648327\" style=\"width: 501px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-648327\" class=\"wp-image-648327 size-full\" src=\"https:\/\/developers.redhat.com\/blog\/wp-content\/uploads\/2019\/11\/diagram-3.png\" alt=\"Diagram 3 - decoupling with Debezium and Camel\" width=\"491\" height=\"429\" srcset=\"https:\/\/developers.redhat.com\/blog\/wp-content\/uploads\/2019\/11\/diagram-3.png 491w, https:\/\/developers.redhat.com\/blog\/wp-content\/uploads\/2019\/11\/diagram-3-300x262.png 300w\" sizes=\"auto, (max-width: 491px) 100vw, 491px\" \/><\/p>\n<p id=\"caption-attachment-648327\" class=\"wp-caption-text\">Figure 3: Decoupling with Debezium and Camel.<\/p>\n<\/div>\n<p>Debezium is in charge of capturing any data change happening in the <code>Order<\/code> domain and publishing it to a <em>topic<\/em>. Then a Camel consumer can pick that event and make a REST call to the <code>User<\/code> API to perform the necessary action expected by its domain (in our simple case, update the list).<\/p>\n<h2>Decoupling with Debezium and Camel<\/h2>\n<p>I&#8217;ve prepared a simple demo with all of the components we need to run the example above. You can find this demo\u00a0<a href=\"https:\/\/github.com\/squakez\/debezium-camel-demo\" target=\"_blank\" rel=\"noopener noreferrer\">in this GitHub repo<\/a>. The only part we need to develop is represented by the following source code:<\/p>\n<pre>public class MyRouteBuilder extends RouteBuilder { public void configure() { from(\"debezium:mysql?name=my-sql-connector\" + \"&amp;databaseServerId=1\" + \"&amp;databaseHostName=localhost\" + \"&amp;databaseUser=debezium\" + \"&amp;databasePassword=dbz\" + \"&amp;databaseServerName=my-app-connector\" + \"&amp;databaseHistoryFileName=\/tmp\/dbhistory.dat\" + \"&amp;databaseWhitelist=debezium\" + \"&amp;tableWhitelist=debezium._order\" + \"&amp;offsetStorageFileName=\/tmp\/offset.dat\") .choice() .when(header(DebeziumConstants.HEADER_OPERATION).isEqualTo(\"c\")) .process(new AfterStructToOrderTranslator()) .to(\"rest-swagger:http:\/\/localhost:8082\/v2\/api-docs#addOrderUsingPOST\") .when(header(DebeziumConstants.HEADER_OPERATION).isEqualTo(\"d\")) .process(new BeforeStructToOrderTranslator()) .to(\"rest-swagger:http:\/\/localhost:8082\/v2\/api-docs#deleteOrderUsingDELETE\") .log(\"Response : ${body}\"); } } <\/pre>\n<p>That&#8217;s it. Really. We don&#8217;t need anything else.<\/p>\n<p>Apache Camel has a <a href=\"https:\/\/camel.apache.org\/components\/latest\/debezium-mysql-component.html\">Debezium component<\/a> that can hook up a MySQL database and use <a href=\"https:\/\/debezium.io\/documentation\/reference\/0.9\/operations\/embedded.html\" target=\"_blank\" rel=\"noopener noreferrer\">Debezium embedded engine<\/a>. The source endpoint configuration provides the parameters needed by Debezium to note any change happening in the <code>debezium._order<\/code> table.\u00a0Debezium streams the events according to a JSON-defined format, so you know what kind of <a href=\"https:\/\/debezium.io\/documentation\/reference\/0.10\/connectors\/mysql.html#events\" target=\"_blank\" rel=\"noopener noreferrer\">information to expect<\/a>. For each event, you will get the information as it was <em>before<\/em> and <em>after<\/em> the event occurs, plus a few useful pieces of meta-information.<\/p>\n<p>Thanks to Camel&#8217;s content-based router, we can either call the <code>addOrderUsingPOST<\/code> or <code>deleteOrderUsingDELETE<\/code> operation. You only have to develop a message translator that can convert the message coming from Debezium:<\/p>\n<pre>public class AfterStructToOrderTranslator implements Processor { private static final String EXPECTED_BODY_FORMAT = \"{\\\"userId\\\":%d,\\\"orderId\\\":%d}\"; public void process(Exchange exchange) throws Exception { final Map value = exchange.getMessage().getBody(Map.class); \/\/ Convert and set body int userId = (int) value.get(\"user_id\"); int orderId = (int) value.get(\"order_id\"); exchange.getIn().setHeader(\"userId\", userId); exchange.getIn().setHeader(\"orderId\", orderId); exchange.getIn().setBody(String.format(EXPECTED_BODY_FORMAT, userId, orderId)); } } <\/pre>\n<p>Notice that we did not touch any of the base code for\u00a0<code>Order<\/code> or <code>User<\/code>. Now, turn off the Debezium process to simulate downtime. You will see that it can recover all events as soon as it turns back on!<\/p>\n<p>You can run the example provided by<a href=\"https:\/\/github.com\/squakez\/debezium-camel-demo\" target=\"_blank\" rel=\"noopener noreferrer\"> following the instructions on this GitHub repo<\/a>.<\/p>\n<h2>Caveat<\/h2>\n<p>The example illustrated here uses Debezium&#8217;s embedded mode. For more consistent solutions, consider using the Kafka connect mode instead, or <a href=\"https:\/\/debezium.io\/documentation\/reference\/0.10\/operations\/embedded.html#_handling_failures\" target=\"_blank\" rel=\"noopener noreferrer\">tuning the embedded engine<\/a> accordingly.<\/p>\n<p><a class=\"a2a_button_facebook\" href=\"https:\/\/www.addtoany.com\/add_to\/facebook?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F19%2Fdecoupling-microservices-with-apache-camel-and-debezium%2F&amp;linkname=Decoupling%20microservices%20with%20Apache%20Camel%20and%20Debezium\" title=\"Facebook\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><\/a><a class=\"a2a_button_twitter\" href=\"https:\/\/www.addtoany.com\/add_to\/twitter?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F19%2Fdecoupling-microservices-with-apache-camel-and-debezium%2F&amp;linkname=Decoupling%20microservices%20with%20Apache%20Camel%20and%20Debezium\" title=\"Twitter\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><\/a><a class=\"a2a_button_linkedin\" href=\"https:\/\/www.addtoany.com\/add_to\/linkedin?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F19%2Fdecoupling-microservices-with-apache-camel-and-debezium%2F&amp;linkname=Decoupling%20microservices%20with%20Apache%20Camel%20and%20Debezium\" title=\"LinkedIn\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><\/a><a class=\"a2a_button_email\" href=\"https:\/\/www.addtoany.com\/add_to\/email?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F19%2Fdecoupling-microservices-with-apache-camel-and-debezium%2F&amp;linkname=Decoupling%20microservices%20with%20Apache%20Camel%20and%20Debezium\" title=\"Email\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><\/a><a class=\"a2a_button_print\" href=\"https:\/\/www.addtoany.com\/add_to\/print?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F19%2Fdecoupling-microservices-with-apache-camel-and-debezium%2F&amp;linkname=Decoupling%20microservices%20with%20Apache%20Camel%20and%20Debezium\" title=\"Print\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><\/a><a class=\"a2a_button_reddit\" href=\"https:\/\/www.addtoany.com\/add_to\/reddit?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F19%2Fdecoupling-microservices-with-apache-camel-and-debezium%2F&amp;linkname=Decoupling%20microservices%20with%20Apache%20Camel%20and%20Debezium\" title=\"Reddit\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><\/a><a class=\"a2a_button_flipboard\" href=\"https:\/\/www.addtoany.com\/add_to\/flipboard?linkurl=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F19%2Fdecoupling-microservices-with-apache-camel-and-debezium%2F&amp;linkname=Decoupling%20microservices%20with%20Apache%20Camel%20and%20Debezium\" title=\"Flipboard\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><\/a><a class=\"a2a_dd addtoany_share_save addtoany_share\" href=\"https:\/\/www.addtoany.com\/share#url=https%3A%2F%2Fdevelopers.redhat.com%2Fblog%2F2019%2F11%2F19%2Fdecoupling-microservices-with-apache-camel-and-debezium%2F&#038;title=Decoupling%20microservices%20with%20Apache%20Camel%20and%20Debezium\" data-a2a-url=\"https:\/\/developers.redhat.com\/blog\/2019\/11\/19\/decoupling-microservices-with-apache-camel-and-debezium\/\" data-a2a-title=\"Decoupling microservices with Apache Camel and Debezium\"><img decoding=\"async\" src=\"https:\/\/static.addtoany.com\/buttons\/favicon.png\" alt=\"Share\"><\/a><\/p>\n<p>The post <a rel=\"nofollow\" href=\"https:\/\/developers.redhat.com\/blog\/2019\/11\/19\/decoupling-microservices-with-apache-camel-and-debezium\/\">Decoupling microservices with Apache Camel and Debezium<\/a> appeared first on <a rel=\"nofollow\" href=\"https:\/\/developers.redhat.com\/blog\">Red Hat Developer<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The rise of microservices-oriented architecture brought us new development paradigms and mantras about independent development and decoupling. In such a scenario, we have to deal with a situation where we aim for independence, but we still need to react to state changes in different enterprise domains. I&#8217;ll use a simple and typical example in order [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":107683,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[858,3],"tags":[865,869,866,859,867,868],"class_list":["post-107682","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java-tutorials","category-tutorials","tag-apache-camel","tag-debezium","tag-integration","tag-java","tag-microservices","tag-red-hat-fuse"],"_links":{"self":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/107682","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/comments?post=107682"}],"version-history":[{"count":0,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/107682\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/media\/107683"}],"wp:attachment":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/media?parent=107682"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/categories?post=107682"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/tags?post=107682"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}