Dapr http://blogs.ugidotnet.org/martinobordin/category/Dapr.aspx My adventures in the Dapr world it Martino Bordin Subtext Version 2.6.0.0 Dapr - Observability http://blogs.ugidotnet.org/martinobordin/archive/2024/03/25/dapr-observability.aspx <p>Monitoring a distributed system is important to identify potential failures, security issues, bottlenecks and in general to understand how our application is performing to apply remediations (that could be scaling or redesigning some parts). In a distributed system, with several interconnected components, a dynamic environment, and unreliable networks, this becomes challenging.</p> <p>Dapr, with his runtime running as a "sidecar", is the central point where the instrumentation of monitoring takes place to have <em>distributed tracing</em>, <em>logging</em>, <em>metrics, </em>and <em>health check; a</em>s everything in Dapr, we can just configure these features using a YAML file.</p> <p>In our sample, we'll use Zipkin to collect tracing information, just by specifying the Zipkin endpoint and the sampling rate (0= disable trace, 1 trace all)</p> <blockquote> <pre><font color="#000000">apiVersion: dapr.io/v1alpha kind: Configuration metadata:   name: daprConfig spec:   tracing:     samplingRate: "1"     zipkin:       endpointAddress: "http://zipkin:9411/api/v2/spans"1</font></pre> </blockquote> <pre><br /></pre> <p>In the Zipkin UI, we can see both traces and services dependencies</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQFl0XyOuBQ5dQ/article-inline_image-shrink_1500_2232/0/1682941020215?e=1717027200&amp;v=beta&amp;t=0JvpC5eCyfJqTd9TjWfIT-QVZdFm5MNfsqfQa8rNgNA" width="931" height="350" /></p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQH2yJ2Vets2bw/article-inline_image-shrink_1500_2232/0/1682943880502?e=1717027200&amp;v=beta&amp;t=sZB3Hq_s6ysgMnHRqehNVzfmH1cmb0nw07O4JtJI204" width="554" height="312" /></p> <p>Dapr can produce structured logs in plain text (the default, that also sends it to <em>stdout</em>) or JSON (better for storing them in external services like ELK).</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQGqAziRwmyxeA/article-inline_image-shrink_1000_1488/0/1682944972960?e=1717027200&amp;v=beta&amp;t=jVOD7LaIam-VdQWpT9ZdCcGShKb13X2-hC-WQ1HHmqk" /></p> <p>We can also use Open Telemetry Collector, Jaeger, and other tools like New Relic and App Insights.</p> <p>See you in the next article for the last one of this series.</p><img src="http://blogs.ugidotnet.org/martinobordin/aggbug/102642.aspx" width="1" height="1" /> Martino Bordin http://blogs.ugidotnet.org/martinobordin/archive/2024/03/25/dapr-observability.aspx Mon, 25 Mar 2024 10:46:35 GMT http://blogs.ugidotnet.org/martinobordin/archive/2024/03/25/dapr-observability.aspx#feedback http://blogs.ugidotnet.org/martinobordin/comments/commentRss/102642.aspx http://blogs.ugidotnet.org/martinobordin/services/trackbacks/102642.aspx Dapr - Resiliency http://blogs.ugidotnet.org/martinobordin/archive/2024/02/05/dapr-resiliency.aspx <p>As we know, in a distributed system we have to take into account more complexity and deal with its <a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing">fallacies</a>. Since several moving parts could potentially be broken or be unreachable, we must design upfront our application with resiliency in mind, to minimize the impact of bad events and continue operating.</p> <p>Dapr gives us out-of-the-box features that allow our applications to handle temporary failures.</p> <p>All we have to do is to create a YAML file where we define <a href="https://docs.dapr.io/operations/resiliency/policies/">policies</a> and <a href="https://docs.dapr.io/operations/resiliency/targets/">targets</a>.</p> <p>Policies are how we want to handle errors:</p> <ul> <li><strong>Timeout</strong>,  to terminate operations after defined intervals </li> <li><strong>Circuit breaking</strong>, to 'open' the circuit when there are several errors in order not to flood the system and give it time to restart the failing service </li> <li><strong>Retry</strong>, to retry failed operations</li> </ul> <p>Targets are where we want to apply such policies:</p> <ul> <li><strong>Apps</strong>, our services (defined in docker-compose in our sample) </li> <li><strong>Components</strong>, the different building blocks of Dapr </li> <li><strong>Actors</strong>, the actor operations (not used in our sample)</li> </ul> <p>I won't post the full YAML specification in this article, but I prepared a full example with comments in the GitHub repository.</p> <p>Check it out <a href="https://github.com/martinobordin/MicroDelivery/blob/master/dapr/components/resiliency.yaml">here</a>.</p> <p>Once done, we configure the Dapr sidecar to load the resiliency configuration using the <a href="invalid://">-resources-path</a> argument.</p> <p><img src="https://media.licdn.com/dms/image/D4D12AQENJxKq_SjBqQ/article-cover_image-shrink_600_2000/0/1687848229433?e=1712793600&amp;v=beta&amp;t=BKa0nbOe8efIy7f3Y4kUxCoJAeRdSnXzbDti06sRoGQ" width="656" height="172" /></p> <p>In the next article, we'll see observability in Dapr</p><img src="http://blogs.ugidotnet.org/martinobordin/aggbug/102641.aspx" width="1" height="1" /> Martino Bordin http://blogs.ugidotnet.org/martinobordin/archive/2024/02/05/dapr-resiliency.aspx Mon, 05 Feb 2024 10:27:24 GMT http://blogs.ugidotnet.org/martinobordin/archive/2024/02/05/dapr-resiliency.aspx#feedback http://blogs.ugidotnet.org/martinobordin/comments/commentRss/102641.aspx http://blogs.ugidotnet.org/martinobordin/services/trackbacks/102641.aspx Dapr - Secret management http://blogs.ugidotnet.org/martinobordin/archive/2024/01/12/dapr-secret-management.aspx <p>Dapr Secrets management is a feature that provides a secure and scalable way to manage application secrets, such as API keys, passwords, and tokens. You can use different cloud providers and environments such as Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, or Kubernetes Secrets.</p> <p>In our example, we'll use a JSON file (not recommended in production!) to store the following secrets:</p> <blockquote> <pre><font color="#000000">"httpbindingtoken": "my auth token",   "ConnectionStrings": {     "SqlServer": "Server=sqlserver;Database=CustomersDb;User Id=sa;Password=admin12345!;TrustServerCertificate=true",     "PostgreSQL": "Server=postgres;Port=5432;Database=ProductsDb;User Id=admin;Password=admin12345;",     "MongoDb": "mongodb://mongo:27017"   },   "MongoDb": {     "DatabaseName": "OrderDb",     "OrdersCollectionName": "Orders"   } }</font></pre> </blockquote> <pre><font color="#000000"><br /></font></pre> <p>We have two way to retrieve them:</p> <ul> <li><strong>declaratively</strong>, in the components spec files </li> <li><strong>programmatically</strong>, using the Dapr APIs</li> </ul> <p>We had a glimpse of the first approach in the previous article, where we configured the Http Output Binding component.</p> <p>Quick recall:</p> <blockquote> <pre><font color="#000000">apiVersion: dapr.io/v1alph kind: Component metadata: name: httpbinding spec: type: bindings.http version: v1 metadata: - name: url value: http://echorestbot.azurewebsites.net/microdelivery - name: securityToken secretKeyRef: name: httpbindingtoken key: httpbindingtoken - name: securityTokenHeader value: "Authorization" auth: secretStore: localsecretstore</font></pre> </blockquote> <p>Here we are configuring some metadata and you may notice that for the item <strong>securityToken </strong>instead of directly inserting the value, we are declaring that we want to use the key <strong>httpbindingtoken </strong>retrieve in <strong>localsecretstore</strong><em>.</em></p> <p><strong>Localsecretstore </strong>is the name of the component for secret management, configured as usual in the YAML file</p> <blockquote> <pre><font color="#000000">apiVersion: dapr.io/v1alpha kind: Component metadata:   name: localsecretstore spec:   type: secretstores.local.file   version: v1   metadata:   - name: secretsFile     value: components/secrets.json   - name: nestedSeparator     value: ":"</font></pre> </blockquote> <p>Here we are basically declaring where we store the secrets (in the file <strong>secrets.json</strong>) and what is the separator character for nested configuration (colon is the default).</p> <p>To use it programmatically, we have to add the Nuget package <strong>Dapr.Extensions.Configuration</strong> in our project and register it with the following lines of code</p> <blockquote> <pre><font color="#000000">var builder = WebApplication.CreateBuilder(args) builder.Configuration.AddDaprSecretStore( "localsecretstore", new DaprClientBuilder().Build(), new[] { ":" });</font></pre> </blockquote> <pre><br /></pre> <pre>Once done, we can access our secret simply using the standard <em>IConfiguration.</em></pre> <pre><em><br /></em></pre> <blockquote> <pre><font color="#000000">public OrdersRepository(IConfiguration configuration { var clientSettings = MongoClientSettings .FromConnectionString(configuration .GetConnectionString("MongoDb"));    var client = new MongoClient(clientSettings);    var database = client .GetDatabase(configuration .GetValue&lt;string&gt;("MongoDb:DatabaseName")); this.orders = database.GetCollection&lt;Order&gt;(configuration .GetValue&lt;string&gt;("MongoDb:OrdersCollectionName")); })</font></pre> </blockquote> <pre><br /></pre> <p>That's it!</p> <p>In the next article, we'll see how to leverage the resiliency feature of Dapr.</p><img src="http://blogs.ugidotnet.org/martinobordin/aggbug/102640.aspx" width="1" height="1" /> Martino Bordin http://blogs.ugidotnet.org/martinobordin/archive/2024/01/12/dapr-secret-management.aspx Fri, 12 Jan 2024 15:29:02 GMT http://blogs.ugidotnet.org/martinobordin/archive/2024/01/12/dapr-secret-management.aspx#feedback http://blogs.ugidotnet.org/martinobordin/comments/commentRss/102640.aspx http://blogs.ugidotnet.org/martinobordin/services/trackbacks/102640.aspx Dapr - Pub/sub &amp; Output Binding http://blogs.ugidotnet.org/martinobordin/archive/2023/12/06/dapr-pubsub-amp-output-binding.aspx <p>In the last article, once created an order we published a message, called <strong>OrderSubmittedIntegrationEvent </strong>with all the relevant information.</p> <p>How can we subscribe to this event in other services?</p> <p>In Dapr there are two ways:</p> <ul> <li><strong>declaratively</strong>, where subscriptions are defined in an external file </li> <li><strong>programmatically</strong>, where subscriptions are defined in our code</li> </ul> <p>We're going to use the second method in the <strong>Notifications </strong>and <strong>Shipping</strong> microservices along with the <strong>Output binding </strong>building block</p> <p>What is an Output binding? It's a way to invoke external resources just by passing a payload and additional metadata.</p> <p>Let's see them in action!</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQFZv34VwWq4zA/article-inline_image-shrink_1500_2232/0/1681579684758?e=1707350400&amp;v=beta&amp;t=uoWlwsHnPogtNnIMSWtxQBaphTKWn6zzzLACs5XCT28" /></p> <p>Notifications microservice</p> <p>The notification microservice will have just one method that is going to use the <a href="https://docs.dapr.io/reference/components-reference/supported-bindings/smtp/">SMTP output binding</a> to send an email to inform the customer that the order has been confirmed and what discount has been applied.</p> <p>Here is the stripped version of the code, check <a href="https://github.com/martinobordin/MicroDelivery/blob/master/src/MicroDelivery.Notifications.Api/Controllers/NotificationsController.cs">GitHub</a> for the complete one.</p> <blockquote> <pre><font color="#000000">[HttpPost [Topic("rabbitmqpubsub", "OrderSubmittedEventTopic")] public async Task&lt;ActionResult&gt; OnOrderSubmittedEventAsync( OrderSubmittedIntegrationEvent orderSubmittedIntegrationEvent) { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"Hello {orderSubmittedIntegrationEvent.CustomerFirstName} {orderSubmittedIntegrationEvent.CustomerLastName}&lt;br&gt;"); stringBuilder.AppendLine($"Your order &lt;strong&gt;#{orderSubmittedIntegrationEvent.OrderId.ToString()[..6]}&lt;/strong&gt; has been shipped.&lt;br&gt;&lt;br&gt;"); stringBuilder.AppendLine($"Your CRAZY DISCOUNT is &lt;strong&gt;#{orderSubmittedIntegrationEvent.TotalDiscount}%&lt;/strong&gt;!&lt;br&gt;&lt;br&gt;"); var message = stringBuilder.ToString(); var metadata = new Dictionary&lt;string, string&gt; { { "emailTo", orderSubmittedIntegrationEvent.CustomerEmail }, { "subject", $"Order Shipped!" }, { "priority", "1" } }; await this.daprClient .InvokeBindingAsync( "smtpbinding", "create", message, metadata); return Ok(); }]</font></pre> </blockquote> <p>The first thing you can notice is that we're using the <strong>Topic</strong> attribute to subscribe to the topic <strong>OrderSubmittedEventTopic </strong>on the <strong>rabbitmqpubsub </strong>component.</p> <p>Dapr is going to invoke this method once a new message is published to that topic, and we receive the payload as a parameter (<strong>OrderSubmittedIntegrationEvent</strong>). We then use the payload to create an email and send it using the <strong>InvokeBindingAsync</strong>, passing as a parameter the name of the binding component (<strong>smtpbinding</strong>), the body of the email, and some metadata (subject, mail address, priority).</p> <p>Of course, we have to configure the binding in the YAML specification:</p> <pre>apiVersion: dapr.io/v1alpha kind: Component metadata:   name: smtpbinding spec:   type: bindings.smtp   version: v1   metadata:   - name: host     value: "smtp4dev"   - name: port     value: "25"   - name: emailFrom     value: "no-reply@microdelivery.com"</pre> <p>For testing purposes, I'm using an email server called <a href="https://github.com/rnwood/smtp4dev">smtp4dev</a> hosted in docker to send and check emails.</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQH8TybGkQ3gdA/article-inline_image-shrink_1500_2232/0/1681581762546?e=1707350400&amp;v=beta&amp;t=gm03f2c5e-E8L_PBL0Bar1iJFVGcp4q9p_opnOvMJ6Y" /></p> <p>The smtp4dev User Interface</p> <p>Now let's see the shippings service.</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQGNAIabPnjIpQ/article-inline_image-shrink_1500_2232/0/1681582440338?e=1707350400&amp;v=beta&amp;t=nqWrW5AHXLa2heNpnP4MbXQ0ixuyh5mBUxNJ-4sMnkE" /></p> <p>Shipping microservice</p> <p>All the logic is in the method <strong>OnOrderSubmittedEvent</strong>, where we're subscribing to the topic <strong>OrderSubmittedEventTopic, </strong>and we're performing an HTTP request using the <a href="https://docs.dapr.io/reference/components-reference/supported-bindings/http/">HTTP Output Binding</a> (to simulate a call to an external supplier). Once done, we publish the <strong>OrderShippedIntegrationEvent </strong>that will be handled by the <strong>Orders </strong>microservice to mark the order as shipped.</p> <blockquote> <pre><font color="#000000">[HttpPost [Topic("rabbitmqpubsub", "OrderSubmittedEventTopic")] public async Task&lt;ActionResult&gt; OnOrderSubmittedEvent( OrderSubmittedIntegrationEvent orderSubmittedIntegrationEvent) { await this.daprClient.InvokeBindingAsync( "httpbinding", "post", orderSubmittedIntegrationEvent); var orderShippedIntegrationEvent = new OrderShippedIntegrationEvent() { OrderId = orderSubmittedIntegrationEvent.OrderId, ShippedAtUtc = DateTime.UtcNow }; await daprClient.PublishEventAsync( "rabbitmqpubsub", "OrderShippedEventTopic", orderShippedIntegrationEvent); return Ok(); }]</font></pre> </blockquote> <p>As in the notifications microservice, we use the <strong>InvokeBindingAsync</strong>, this time to call the HTTP endpoint, passing the <strong>orderSubmittedIntegrationEvent </strong>as payload.</p> <p>The binding is configured with the following YAML specification:</p> <blockquote> <pre><font color="#000000">apiVersion: dapr.io/v1alpha kind: Component metadata: name: httpbinding spec: type: bindings.http version: v1 metadata: - name: url value: http://echorestbot.azurewebsites.net/microdelivery - name: securityToken secretKeyRef: name: httpbindingtoken key: httpbindingtoken - name: securityTokenHeader value: "Authorization" auth: secretStore: localsecretstore</font></pre> </blockquote> <p>Here you see that we want to perform a POST request to the URL <a href="http://echorestbot.azurewebsites.net/microdelivery">http://echorestbot.azurewebsites.net/microdelivery</a> (it's a <a href="https://github.com/martinobordin/EchoREST">simple API</a> I built that gives you back the request details). We are also passing a token in the "Authorization" header; the value of the token is read by using the secret management build block (more info in the next article).</p> <p>Here is an example of the request we made to the EchoRest endpoint, where you can see the payload containing the order's detail:</p> <blockquote> <pre><font color="#000000">{   "RequestIdentifier": "14085696-218e-42f2-8966-7a0b37b506ea",   "Date": "2023-05-01T20:02:07.2657896Z",   "Url": "http://echorestbot.azurewebsites.net/microdelivery",   "Body": "{\"orderId\":\"307998cc-53c9-46cf-9dca-06df25419496\",\"customerId\":1,\"customerFirstName\":\"Joe\",\"customerLastName\":\"Doe\",\"customerEmail\":\"joe.doe@email.com\",\"orderLineItems\":[{\"productId\":1,\"productName\":\"Tomato salad\",\"quantity\":3,\"price\":8,\"discountedPrice\":8},{\"productId\":2,\"productName\":\"Margherita\",\"quantity\":3,\"price\":6,\"discountedPrice\":6}],\"totalDiscount\":0,\"eventId\":\"823132ef-197a-4fc3-8da4-175966bae8dc\"}",   "Headers": [     {       "Key": "Accept",       "Value": "application/json; charset=utf-8"     },     {       "Key": "Host",       "Value": "echorestbot.azurewebsites.net"     },        {       "Key": "Authorization",       "Value": "my auth token"     },     {       "Key": "Content-Type",       "Value": "application/json; charset=utf-8"     },     {       "Key": "Content-Length",       "Value": "412"     }          ],   "Method": "POST",   "Path": {     "Value": "/microdelivery",     "HasValue": true   } }</font></pre> </blockquote> <p>After calling the sample endpoint, we're going to publish an <strong>OrderShippedIntegrationEvent </strong>that will be handled by the Order's microservice to update the order, just subscribing to that event using the <strong>Topic </strong>attribute we already saw.</p> <blockquote> <pre><font color="#000000">[HttpPost("Ship") [Topic("rabbitmqpubsub", "OrderShippedEventTopic")] public async Task&lt;ActionResult&gt; OnOrderShippedEvent(OrderShippedIntegrationEvent orderShippedIntegrationEvent) { var order = await this.orderRepository .GetOrderAsync(orderShippedIntegrationEvent.OrderId); if (order is null) { return NotFound(); } order.ShippedAtUtc = orderShippedIntegrationEvent.ShippedAtUtc; await this.orderRepository.UpdateOrderAsync(order); return Ok(); }]</font></pre> </blockquote> <p>In the next article, we'll discuss about secret management with DAPR.</p><img src="http://blogs.ugidotnet.org/martinobordin/aggbug/102639.aspx" width="1" height="1" /> Martino Bordin http://blogs.ugidotnet.org/martinobordin/archive/2023/12/06/dapr-pubsub-amp-output-binding.aspx Wed, 06 Dec 2023 16:53:17 GMT http://blogs.ugidotnet.org/martinobordin/archive/2023/12/06/dapr-pubsub-amp-output-binding.aspx#feedback http://blogs.ugidotnet.org/martinobordin/comments/commentRss/102639.aspx http://blogs.ugidotnet.org/martinobordin/services/trackbacks/102639.aspx Dapr - Service invocation &amp; Pub/sub http://blogs.ugidotnet.org/martinobordin/archive/2023/11/04/dapr-service-invocation-amp-pubsub.aspx <p>Dapr <a href="https://docs.dapr.io/developing-applications/building-blocks/service-invocation/service-invocation-overview/">service discovery &amp; invocation</a> is a feature that allows services to discover and call other services using HTTP or gRPC protocols, providing a simple and consistent way to invoke them.</p> <p>Communication is also</p> <ul> <li>secure (with mutual mTLS authentication) </li> <li>resilient (with configurable retry\circuit breaker\timeout policies) </li> <li>traced and metered (using common protocols) </li> <li>controlled (we can use ACL to restrict accesses\permissions to some APIs) </li> <li>balanced (using round-robin)</li> </ul> <p>The pub/sub is a messaging pattern where a message is published by a sender to a topic, and all the subscribers who have subscribed to that topic will receive a copy of the message. This allows loosely coupled communication between services, as the sender does not need to know who the receivers are or how many there are, and the subscribers do not need to know who the publisher is. </p> <p>The <a href="https://docs.dapr.io/developing-applications/building-blocks/pubsub/pubsub-overview/">pub/sub API in DAPR</a> provides a set of APIs that can be used to implement this pattern and allows developers to use different message brokers like Apache Kafka, RabbitMQ, Azure Service Bus, and many others. The API is platform-agnostic and offers an at least-once message delivery guarantee.</p> <p>Let's see how we can use both functionalities in the orders microservices.</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQFJ3X0LgtKEPw/article-inline_image-shrink_1500_2232/0/1681481079387?e=1704326400&amp;v=beta&amp;t=XfdVOt80FHKi4nlry6aEpDRJj9GTS6dPkNyq4zhCdvg" /></p> <p>As always, we have a standard API controller where we inject the <strong>DaprClient</strong>.</p> <p>The relevant code is in the <strong>SubmitOrder.</strong></p> <p>Essentially, what we want to do is:</p> <ul> <li>retrieve customer info (calling the customer's microservice) </li> <li>apply the current valid discount (calling the discount microservice) </li> <li>retrieve products info </li> <li>create and save a new order </li> <li>publish a message <strong>OrderSubmittedIntegrationEvent</strong></li> </ul> <p><strong /></p> <blockquote> <pre><font color="#000000">[HttpPost("Submit") [ProducesResponseType(typeof(Order), (int)HttpStatusCode.Created)] public async Task&lt;ActionResult&gt; SubmitOrder(SubmitOrderRequest request) { var customerInfo = await daprClient .InvokeMethodAsync&lt;CustomerInfo&gt;( HttpMethod.Get, "microdelivery-customers-api", $"customers/{request.CustomerId}"); if (customerInfo is null) { throw new Exception($"Customer {request.CustomerId} not found"); } var discount = await daprClient .InvokeMethodAsync&lt;int&gt;( HttpMethod.Get, "microdelivery-discount-api", "discount"); var order = new Order { CustomerId = customerInfo.Id, CustomerFirstName = customerInfo.FirstName, CustomerLastName = customerInfo.LastName, CustomerEmail = customerInfo.Email, TotalDiscount = discount }; var orderLineItems = new List&lt;OrderLineItem&gt;(); foreach (var requestOrderLineItem in request.OrderLineItems) { var productInfo = await daprClient .InvokeMethodAsync&lt;ProductInfo&gt;( HttpMethod.Get, "microdelivery-products-api", $"products/{requestOrderLineItem.ProductId}"); if (productInfo is null) { throw new Exception($"Product {requestOrderLineItem.ProductId} not found"); } var discountedPrice = discount == 0 ? productInfo.Price : productInfo.Price - (productInfo.Price * ((double)discount / 100)); var orderLineItem = new OrderLineItem { ProductId = productInfo.Id, ProductName = productInfo.Name, Price = productInfo.Price, DiscountedPrice = discountedPrice, Quantity = requestOrderLineItem.Quantity }; orderLineItems.Add(orderLineItem); } order.OrderLineItems = orderLineItems; await orderRepository.CreateOrderAsync(order); var orderSubmittedIntegrationEvent = new OrderSubmittedIntegrationEvent { OrderId = order.Id, CustomerId = order.CustomerId, CustomerFirstName = order.CustomerFirstName, CustomerLastName = order.CustomerLastName, CustomerEmail = order.CustomerEmail, TotalDiscount = order.TotalDiscount, OrderLineItems = order.OrderLineItems.Select(oli =&gt; new OrderSubmittedIntegrationEventLineItem { ProductId = oli.ProductId, ProductName = oli.ProductName, Quantity = oli.Quantity, Price = oli.Price, DiscountedPrice = oli.DiscountedPrice }) }; await daprClient.PublishEventAsync( "rabbitmqpubsub", "OrderSubmittedEventTopic", orderSubmittedIntegrationEvent); return CreatedAtAction(nameof(GetOrder), new { id = order.Id }, order); }</font></pre> </blockquote> <pre><br /></pre> <p>For the service invocation, we use the method <strong>InvokeMethodAsync&lt;T&gt; </strong>of the<strong> DaprClient</strong>.</p> <p>We have to specify the HTTP Verb to use, the application id of the other service (we set it on the sidecar configuration in the docker-compose file), and the method name. If the call is successful, we obtain the response of type T. That's simple.</p> <p>To publish a message, we use the method <strong>PublishEventAsync&lt;T&gt; </strong>of the <strong>DaprClient</strong>.</p> <p>We have to specify the name of the pubsub component, the topic where we want to publish the message, and the message payload.</p> <p>In our example, we're going to use RabbitMq, so here is the component specification:</p> <blockquote> <pre><font color="#000000">apiVersion: dapr.io/v1alpha kind: Component metadata:   name: rabbitmqpubsub spec:   type: pubsub.rabbitmq   version: v1   metadata:   - name: connectionString     value: "amqp://rabbit:5672"   - name: hostname     value: rabbit    - name: username     value: guest   - name: password     value: guest </font></pre> </blockquote> <pre><br /></pre> <pre>In the next article, we'll see how other services can subscribe to the published event in order to perform their logic.</pre><img src="http://blogs.ugidotnet.org/martinobordin/aggbug/102637.aspx" width="1" height="1" /> Martino Bordin http://blogs.ugidotnet.org/martinobordin/archive/2023/11/04/dapr-service-invocation-amp-pubsub.aspx Sat, 04 Nov 2023 12:39:13 GMT http://blogs.ugidotnet.org/martinobordin/archive/2023/11/04/dapr-service-invocation-amp-pubsub.aspx#feedback http://blogs.ugidotnet.org/martinobordin/comments/commentRss/102637.aspx http://blogs.ugidotnet.org/martinobordin/services/trackbacks/102637.aspx Dapr - Input Binding and Configuration http://blogs.ugidotnet.org/martinobordin/archive/2023/10/10/dapr-input-binding-and-configuration.aspx <p>Dapr binding is a mechanism that allows connections between components and external services by providing a common interface for communication, without needing to know about each other's implementation details.</p> <p>It allows components to send and receive messages over a range of transport protocols and message brokers, like RabbitMQ and Kafka, Azure Event Grid, AWS SNS, Azure Service Bus, and Amazon SQS. You can also integrate with Twitter, SMTP, SendGrid, HTTP, or CRON.</p> <p>Dapr has a building block also to manage and retrieve the application configurations (settings, connection strings, identifiers, etc) just by configuring the related component.</p> <p>At the moment, the supported configuration stores are Redis, Postgres, and Azure App configuration.</p> <p>Let's see how we can use both functionalities in the discount microservices.</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQHp-_s1PT-Piw/article-inline_image-shrink_1500_2232/0/1681473637823?e=1702512000&amp;v=beta&amp;t=Zct7aIDDeegg0ovJFohGcOucXX56icn7q_7svSek_eA" /></p> <p>First of all, we have to reference an additional NuGet package: <strong>Dapr.Extensions.Configuration</strong></p> <p>Once done, just go to the Program.cs file and add these lines of code, where we declare the config store and the list of keys we want to retrieve.</p> <blockquote> <pre><font color="#000000">var builder = WebApplication.CreateBuilder(args var client = new <strong>DaprClientBuilder</strong>().Build(); builder.Configuration     .<strong>AddDaprConfigurationStore</strong>( "redisconfigstore", new List&lt;string&gt;() { "CrazyDiscountEnabled" }, client, TimeSpan.FromSeconds(20))     .<strong>AddStreamingDaprConfigurationStore</strong>( "redisconfigstore", new List&lt;string&gt;() { "CrazyDiscountEnabled" }, client, TimeSpan.FromSeconds(20));)</font></pre> </blockquote> <pre><font color="#000000"><br /></font></pre> <p>Please note that <em>AddDaprConfigurationStore </em>will make the first call to load the config with the given keys, while <em>AddStreamingDaprConfigurationStore </em>will keep watching for changes and update local configurations.</p> <p>Here's the main code of the discount controller:</p> <blockquote> <pre><font color="#000000">[ApiController [Route("[controller]")] public class DiscountController : ControllerBase { private readonly DaprClient daprClient; private readonly IConfiguration configuration; public DiscountController( DaprClient daprClient, IConfiguration configuration) { this.daprClient = daprClient; this.configuration = configuration; } [HttpPost("/discountcronbinding")] public async Task&lt;ActionResult&gt; UpdateDiscount() { var crazyDiscountEnabled = configuration .GetValue&lt;bool&gt;("CrazyDiscountEnabled"); if (!crazyDiscountEnabled) { return Ok(); } var random = new Random(Guid.NewGuid().GetHashCode()); var discount = random.Next(1, 30); await daprClient.SaveStateAsync("redisstore", "CrazyDiscountValue", discount); return Ok(); } [HttpGet] public async Task&lt;int&gt; GetDiscount() { var discount = 0;          var crazyDiscountEnabled = configuration .GetValue&lt;bool&gt;("CrazyDiscountEnabled");         if (crazyDiscountEnabled)         {          discount = await daprClient .GetStateAsync&lt;int&gt;("redisstore", "CrazyDiscountValue");         } return discount; } }]</font></pre> </blockquote> <p>We have a standard API Controller where we inject the DaprClient and the standard <em>IConfiguration </em>object that allows us to retrieve the settings.</p> <p>The <strong>UpdateDiscount</strong> method checks if the configuration with the key <strong>CrazyDiscountEnabled</strong> is enabled. If yes, we're going to generate a random discount and save it using the state management we already know.</p> <p>How is configured the Config store? Using the component specification, of course!</p> <blockquote> <pre><font color="#000000">apiVersion: dapr.io/v1alpha kind: Component metadata:   name: redisconfigstore spec:   type: configuration.redis   metadata:   - name: redisHost     value: redis:63791</font></pre> </blockquote> <pre><br /></pre> <pre><br /></pre> <pre>As you can see we're connecting to Redis, so we can enable the crazy discount feature by running the following command in the Redis CLI</pre> <pre><br /></pre> <blockquote> <pre><font color="#000000">SET CrazyDiscountEnabled true</font></pre> </blockquote> <p>Now the random discount calculation is enabled, but when and who is calling this API?</p> <p>We're going to use the <a href="https://docs.dapr.io/reference/components-reference/supported-bindings/cron/">CRON input binding</a>, which will trigger a POST request based on the CRON expression we specify.</p> <p>Here is our configuration:</p> <blockquote> <pre><font color="#000000">apiVersion: dapr.io/v1alpha kind: Component metadata:   name: discountcronbinding spec:   type: bindings.cron   version: v1   metadata:   - name: schedule     value: "* * * * * *"</font></pre> </blockquote> <pre><br /></pre> <p>We set the CRON expression in order to trigger every second. The POST will call a method with the same name as the binding (<strong>discountcronbinding </strong>in our case). So, basically, we're calculating a new random discount every second (if enabled by config).</p> <p>We can retrieve the current valid discount just by performing a GET request to the Discount endpoint.</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQGU7RzuwqH6lg/article-inline_image-shrink_1500_2232/0/1681480158825?e=1702512000&amp;v=beta&amp;t=jxITQdtKD2q4yHKqHoyCvP-5XqQe9J6owlMzopTLEdI" /></p> <p>In the next article, we'll see the Pub\Sub building block.</p><img src="http://blogs.ugidotnet.org/martinobordin/aggbug/102636.aspx" width="1" height="1" /> Martino Bordin http://blogs.ugidotnet.org/martinobordin/archive/2023/10/10/dapr-input-binding-and-configuration.aspx Tue, 10 Oct 2023 08:37:44 GMT http://blogs.ugidotnet.org/martinobordin/archive/2023/10/10/dapr-input-binding-and-configuration.aspx#feedback http://blogs.ugidotnet.org/martinobordin/comments/commentRss/102636.aspx http://blogs.ugidotnet.org/martinobordin/services/trackbacks/102636.aspx Dapr - State management http://blogs.ugidotnet.org/martinobordin/archive/2023/09/06/dapr-state-management.aspx <p>Dapr allows the storage of durable data (key\value pairs) across multiple sessions and services.</p> <p>With Dapr State Management, you can:</p> <ul> <li>Save and retrieve state using different stores with 2 levels of consistency (strong and eventual) </li> <li>Use a consistent API to perform operations, abstracting away the implementation details. </li> <li>Implement caching (with TTL) to improve performance. </li> <li>Filter\Sort\Page state entries</li> </ul> <p>Let's see how we can use it in the customer's microservices.</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQHDKs12V7_WGQ/article-inline_image-shrink_1500_2232/0/1681464150319?e=1699488000&amp;v=beta&amp;t=-5fQ12_z-z-IiYVK2ZlQNwtZNtnlNb6Cu7DRwzJy6n4" /></p> <p>The customers' microservice persists data in MS SQL Server and caches data in MongoDB</p> <p>Here's the code:</p> <blockquote> <pre><font color="#000000">public class CustomersController : ControllerBase { private readonly ILogger&lt;CustomersController&gt; logger; private readonly DaprClient daprClient; private readonly ICustomersRepository customerRepository; public CustomersController(ILogger&lt;CustomersController&gt; logger, DaprClient daprClient, ICustomersRepository customerRepository) { this.logger = logger; this.daprClient = daprClient; this.customerRepository = customerRepository; } [HttpGet] [ProducesResponseType(typeof(IEnumerable&lt;Customer&gt;), (int)HttpStatusCode.OK)] public async Task&lt;ActionResult&lt;IEnumerable&lt;Customer&gt;&gt;&gt; GetCustomers() { var customers = await GetCustomersFromCache(); return Ok(customers); } private async Task&lt;IEnumerable&lt;Customer&gt;&gt; GetCustomersFromCache() { var customers = await daprClient .GetStateAsync&lt;IEnumerable&lt;Customer&gt;&gt;( "mongostore", "GetCustomers"); if (customers == null) { customers = await customerRepository .GetCustomersAsync(); await daprClient .SaveStateAsync("mongostore", "GetCustomers", customers); } return customers; } }</font> </pre> </blockquote> <p>We have a standard API Controller where we inject the <strong>DaprClient </strong>(view the previous article to see the NuGet package required and how to register it).</p> <p>In the <strong>GetCustomersFromCache </strong>method, we use the <strong>GetStateAsync </strong>to check if we previously cached the data. If yes, we just return it; if no, we load it (using the customer repository), cache it using the <strong>SaveStateAsync </strong>and return it.</p> <p>Please note that we are specifying where we want to save our state by passing the store name (in our case "mongostore"). But where is it defined?</p> <p>If you remember in the previous article we configured the sidecar with the component path; that's the folder where we store the configurations of our components.</p> <p>Therefore, we just have to create under that folder a new YAML file with the following configuration</p> <blockquote> <pre><font color="#000000">apiVersion: dapr.io/v1alpha kind: Component metadata:   name: mongostore spec:   type: state.mongodb   version: v1   metadata:   - name: host     value: "mongo:27017"</font></pre> </blockquote> <p>The value "mongostore" in the metadata section is the store name we have to use in our code (I set it as "mongostore", but you can follow your preferred naming convention). We also specify how to connect to the MongoDB store.</p> <p>Where can you find all these settings? In the <a href="https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-mongodb/">official documentation</a>, of course!</p> <p>Now we have all the information to use another state store (i.e. Redis) as I've done in the products microservice.</p> <p>The products' microservice persists data in PostgreSQL and cache data in Redis</p> <p>The relevant piece of code is similar to the previous one:</p> <blockquote> <pre><font color="#000000">private async Task&lt;IEnumerable&lt;Product&gt;&gt; GetProductsFromCache( { var products = await daprClient .GetStateAsync&lt;IEnumerable&lt;Product&gt;&gt;( "redisstore", "GetProducts"); if (products == null) { products = await this.productRepository.GetProductsAsync(); await daprClient.SaveStateAsync("redisstore", "GetProducts", products); } return products; })</font></pre> </blockquote> <p>To configure the Redis state store, we create the following file in the component's folder</p> <blockquote> <pre><font color="#000000">apiVersion: dapr.io/v1alpha kind: Component metadata:   name: redisstore spec:   type: state.redis   version: v1   metadata:   - name: redisHost     value: redis:6379   - name: redisPassword     value: ""</font></pre> </blockquote> <p>To test both customers' and products' microservices, I prepared 2 <em>.http</em> files with the sample GET requests, so we can use Visual Studio.</p> <p>Just start the solution (using the docker-compose) and executes both requests.</p> <p>When executing the GET /customers, the result will be cached in Mongo</p> <p>In the <a href="https://github.com/martinobordin/MicroDelivery">source code</a> on GitHub, you can see also how to delete the state and how to set a TTL of the cached value (otherwise cache will never expire!).</p> <p>In the next article, we'll see the Configuration and Binding building blocks</p><img src="http://blogs.ugidotnet.org/martinobordin/aggbug/102632.aspx" width="1" height="1" /> Martino Bordin http://blogs.ugidotnet.org/martinobordin/archive/2023/09/06/dapr-state-management.aspx Wed, 06 Sep 2023 15:54:07 GMT http://blogs.ugidotnet.org/martinobordin/archive/2023/09/06/dapr-state-management.aspx#feedback http://blogs.ugidotnet.org/martinobordin/comments/commentRss/102632.aspx http://blogs.ugidotnet.org/martinobordin/services/trackbacks/102632.aspx Dapr - Create a Dapr service http://blogs.ugidotnet.org/martinobordin/archive/2023/07/03/dapr-create-a-dapr-service.aspx <p>In order to use Dapr with C#, you just have to create a new ASP.NET Core Web API Project and reference the Nuget package <strong>Dapr.AspNetCore</strong></p> <p>Once done, just go in the <em>Program.cs</em> and chain the method <strong>AddDapr()</strong> after the method <strong>AddControllers()</strong>:</p> <blockquote> <pre><font color="#000000">builder.Services.AddControllers().AddDapr();</font></pre> </blockquote> <pre><font color="#000000"><br /></font></pre> <pre>In addition to that, just call these methods to register the Pub\Sub building block:</pre> <pre><br /></pre> <blockquote> <pre><font color="#000000">app.MapSubscribeHandler(); app.UseCloudEvents();</font></pre> </blockquote> <pre><br /></pre> <pre>At this point, you can inject the <strong>DaprClient</strong>, in your controller or services<strong>, </strong>to interact with the several APIs of Dapr.</pre> <pre><br /></pre> <blockquote> <pre><font color="#000000">public CustomersController(DaprClient daprClient) { this.daprClient = daprClient; }</font></pre> </blockquote> <pre><br /></pre> <pre><br /></pre> <pre>We then can run the application along with Dapr sidecar using the CLI</pre> <pre><br /></pre> <blockquote> <pre><font color="#000000">dapr run --app-id microdelivery.customers.api --app-port 8000 -- dotnet run</font></pre> </blockquote> <pre><br /></pre> <pre>But, since we're going to create several microservices, I prefer the <a href="https://docs.dapr.io/operations/hosting/self-hosted/self-hosted-with-docker/#run-using-docker-compose">docker-compose</a> approach. Here the YAML file for the customer's microservice (<em>microdelivery.customers.api</em>) and its sidercar (<em>microdelivery.customers.api.dapr</em>).</pre> <pre><br /></pre> <blockquote> <pre><font color="#000000"> version: '3.4 services: # Customers Api &amp; Sidecar microdelivery.customers.api: container_name: microdelivery.customers.api image: ${DOCKER_REGISTRY-}microdeliverycustomersapi build: context: . dockerfile: src/MicroDelivery.Customers.Api/Dockerfile depends_on: - sqlserver - redis environment: - ASPNETCORE_ENVIRONMENT=Development ports: - "8000:80" microdelivery.customers.api.dapr: container_name: microdelivery.customers.api.dapr image: "daprio/daprd:latest" network_mode: "service:microdelivery.customers.api" depends_on: - microdelivery.customers.api command: ["./daprd", "-app-id", "microdelivery-customers-api", "-app-port", "80", "-resources-path", "/components", "-config", "/configuration/configuration.yaml", "-log-level", "debug" ] volumes: - "./dapr/components/:/components" - "./dapr/configuration/:/configuration"' </font></pre> </blockquote> <p>The sidecar is a docker image (<em>daprio/daprd</em>) attached to the network of the customer's microservices where we execute the daprd command, passing some parameters like the app id that identifies our app, the app port, what is the components\config path to customize our microservices behavior and the log level.</p> <p>We can then just run the application with Visual Studio that will create the two containers and we're ready to debug it!</p> <p>In the next article, we'll see the first building block: <strong>State management</strong></p><img src="http://blogs.ugidotnet.org/martinobordin/aggbug/102631.aspx" width="1" height="1" /> Martino Bordin http://blogs.ugidotnet.org/martinobordin/archive/2023/07/03/dapr-create-a-dapr-service.aspx Mon, 03 Jul 2023 07:19:37 GMT http://blogs.ugidotnet.org/martinobordin/archive/2023/07/03/dapr-create-a-dapr-service.aspx#feedback http://blogs.ugidotnet.org/martinobordin/comments/commentRss/102631.aspx http://blogs.ugidotnet.org/martinobordin/services/trackbacks/102631.aspx Dapr - The application scenario http://blogs.ugidotnet.org/martinobordin/archive/2023/06/05/dapr-the-application-scenario.aspx <p>In this series of articles (source code is available on <a href="https://github.com/martinobordin/MicroDelivery">GitHub</a>), we'll develop a microservices application for an imaginary food delivery company called "MicroDelivery".</p> <p>We'll have several services, each with its own logic, that will interact via RPC call or pub\sub messaging and we'll see how to use Dapr to build it.</p> <hr /> <p><strong>⚠️ Disclaimer</strong></p> <p> My goal here is to give you a birds-eye view of the different features of Dapr, not to guide you step by step on building an application from scratch, nor to explain the single details of Dapr (there's the official website and several books for that!)</p> <p> I won't explain why and when to adopt a microservices architecture and related pros\cons, nor we'll talk about any design\integrations\architectural patterns.</p> <p>☁️We're not going to use any Cloud Provider (only on-prem docker services), but of course, the nature of Dapr allows you to easily switch from on-prem to the cloud.</p> <p>⚒️ The application leverages several technologies(Redis, RabbitMq, SqlServer, PostgreSQL, MongoDB, Zipkin) and shows you how it's simple to use them together with Dapr. Of course, in the real world, you need to analyze your requirements and understand if these tools are useful for your needs along with their strong and weak points.</p> <hr /> <h5>Customers Microservice</h5> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQHAFHqQPWR6zQ/article-inline_image-shrink_1500_2232/0/1680628260512?e=1699488000&amp;v=beta&amp;t=eQTPc1fRCVieq9qMun3KNJQJqDe7lN95Ey3UY_vSLMk" /></p> <p>Customers Microservice</p> <p>It's a CRUD microservice to manage customers' data.</p> <p>It persists its data in <em>SQL Server</em> using <em>Entity Framework</em> and caches them in <em>MongoDB</em>, using Dapr State block</p> <h5>Products Microservice</h5> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQEKSCWPZelD3g/article-inline_image-shrink_1500_2232/0/1680628274156?e=1699488000&amp;v=beta&amp;t=i1zzNSBkhFzV6lAWDuYNtnjLHFZSF0PZaRQZCmfRDHY" /></p> <p>Products Microservice</p> <p>It's a CRUD microservice to manage products' data.</p> <p>It persists its data in <em>PostgreSQL </em>using <em>Entity Framework</em> and caches them in <em>Redis </em>using Dapr State block</p> <h5>Orders Microservice</h5> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQGDs0MRDxZApA/article-inline_image-shrink_1500_2232/0/1680628286297?e=1699488000&amp;v=beta&amp;t=f-pP8ch8lPIjsAvBMaLDnArM7dAU1MjXsqZSRdtar8M" /></p> <p>Orders Microservice</p> <p>It's a microservice that receives the order requests, performs some, publishes a message (<em>OrderSubmittedEvent</em>) to notify other services, and receives a message (<em>OrderShipped</em>) to mark an order as shipped.</p> <p>It persists its data in <em>MongoDB</em>, calls Discount\ Customers\Products microservices using service-to-service DAPR block, and send\received message in <em>RabbitMQ </em>using Dapr <em>Pub\Sub</em> block,</p> <h5>Discount Microservice</h5> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQHGsgsLGzHmmw/article-inline_image-shrink_1500_2232/0/1680628300423?e=1699488000&amp;v=beta&amp;t=NyJ7jua90hdoP_GUWueAedSM2IDowrI5Obcu0UmHa3o" /></p> <p>Discount Microservice</p> <p>It's a microservice that, if enabled by configuration, calculates a random discount (very funny, isn't it?) that remains valid until the next recalculation.</p> <p>The (re)calculation is triggered by a <em>Dapr CRON Binding</em>, and the configuration is stored on the <em>Redis </em>configuration block. It will be invoked by the Orders microservice using <em>Dapr service-to-service</em> communication,</p> <h5>Notifications Microservice</h5> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D5612AQGfkYGn4EjUbQ/article-inline_image-shrink_1500_2232/0/1680798586612?e=1699488000&amp;v=beta&amp;t=l8JiVFzltWwtcLRVq4vMXOKdHrikjFfWO-xD_frUEJQ" /></p> <p>Notifications Microservice</p> <p>It's a microservice that receives the message <em>OrderSubmittedEvent </em>and sends a confirmation email to customers, using <em>Dapr SMTP binding</em>.</p> <h5>Shipping Microservice</h5> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQGyI2KDYnYMBg/article-inline_image-shrink_1500_2232/0/1680628333186?e=1699488000&amp;v=beta&amp;t=VBI04z0UmH2fPWSWHVEsgDI0WxQAolig5fT8dQIwbLk" /></p> <p>Shipping Microservice</p> <p>It's a microservice that receives the message <em>OrderSubmittedEvent </em>and performs an HTTP call to an external Webhook, using <em>Dapr HTTP binding </em>and reading the Bearer Token from <em>Dapr Secret store</em>. It will also publish an <em>OrderShipped </em>event.</p> <p>Here is the full application diagram</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/martinobordin/Open-Live-Writer/Dapr---The-application-scenario_FF38/123_2.png"><img title="123" style="display: inline; background-image: none;" border="0" alt="123" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/martinobordin/Open-Live-Writer/Dapr---The-application-scenario_FF38/123_thumb.png" width="757" height="966" /></a></p> <p>A full diagram of the sample application</p> <p>The dotted links are the calls to Dapr Sidecar, performed using the C# SDK.</p> <p>In the next article, we'll see how to set up Dapr within our project.</p><img src="http://blogs.ugidotnet.org/martinobordin/aggbug/102633.aspx" width="1" height="1" /> Martino Bordin http://blogs.ugidotnet.org/martinobordin/archive/2023/06/05/dapr-the-application-scenario.aspx Mon, 05 Jun 2023 16:09:00 GMT http://blogs.ugidotnet.org/martinobordin/archive/2023/06/05/dapr-the-application-scenario.aspx#feedback http://blogs.ugidotnet.org/martinobordin/comments/commentRss/102633.aspx http://blogs.ugidotnet.org/martinobordin/services/trackbacks/102633.aspx Dapr - An introduction to the runtime http://blogs.ugidotnet.org/martinobordin/archive/2023/05/29/dapr-an-introduction-to-the-runtime.aspx <p><strong>Dapr</strong>, standing for <strong>D</strong>istributed <strong>Ap</strong>plication <strong>R</strong>untime, is an open-source event-driven runtime that simplifies the building of resilient microservices-based applications.</p> <p>It's designed to work with any programming language, any infrastructure, and any cloud provider (Azure, AWS, GCP) and it's part of the Cloud Native Computing Foundation (<a href="https://www.cncf.io/projects/dapr/">CNCF</a>) project.</p> <p>Its main goal is to abstract the complexity of distributed systems and allows developers to focus on business logic rather than the underlying infrastructure (warning: just because the complexity is 'hidden' doesn't mean you should not be aware of what it is and how you would solve it).</p> <p>These abstractions are called 'Building blocks' and provide APIs for common functionalities like State Management, Service Discovery &amp; Invocation, Publish &amp; Subscribe, Observability, and many others.</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQEE3SsjDLiQiA/article-inline_image-shrink_1000_1488/0/1679478440760?e=1698883200&amp;v=beta&amp;t=EHujRS7Yqdi09r6Ri_PVFnRilSrxNHBdFCmJ9WkZVmc" /></p> <p>The implementation of a building block is called 'component' and there are several of them ready to use, all configurable using external YAML files and all easily interchangeable.</p> <p>For example, for the publish\subscribe feature, we have components for RabbitMQ, Redis, AWS SNS/SQS, or GCP Pub/Sub.</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQGzobInNEC9cQ/article-inline_image-shrink_1000_1488/0/1680528577759?e=1698883200&amp;v=beta&amp;t=6m3i1MncIvpxIQIhrXJoedVcz2uekg1AE_iAxha5lEU" /></p> <p>Dapr will run as an external process\container (called <a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/sidecar">Sidecar</a>) and our application will interact with it using HTTP\GRPC or the SDKs available for the main programming languages (.NET, Java, Node, Python, Go).</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQFSDy_wMIMBgQ/article-inline_image-shrink_1500_2232/0/1679485689192?e=1698883200&amp;v=beta&amp;t=ozjf_RdA8Y71Qx_n3c76-5BBDjPsdHC8aXw8F60vD0o" width="866" height="495" /></p> <p>We can also use a CLI and a Dashboard to initialize, run, and tests Dapr applications.</p> <p><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQHzL-aAu9b3yg/article-inline_image-shrink_1000_1488/0/1679487782486?e=1698883200&amp;v=beta&amp;t=F0dN9nTKU1KSPirKiQPuTGPUM8Cnj7Zwvl9rpGF2Fqs" /><img alt="No alt text provided for this image" src="https://media.licdn.com/dms/image/D4D12AQHEOXMok3DKmg/article-inline_image-shrink_1000_1488/0/1679487820188?e=1698883200&amp;v=beta&amp;t=OlTnOrCwvrth028HVy_vBxPpzjjDwBPq9-baBs2-Ew0" /></p> <p>You can read the full documentation on the <a href="https://dapr.io/">official website</a>.</p> <p>In the next article, we'll see how we can build a sample application using DAPR.</p><img src="http://blogs.ugidotnet.org/martinobordin/aggbug/102635.aspx" width="1" height="1" /> Martino Bordin http://blogs.ugidotnet.org/martinobordin/archive/2023/05/29/dapr-an-introduction-to-the-runtime.aspx Mon, 29 May 2023 16:20:00 GMT http://blogs.ugidotnet.org/martinobordin/archive/2023/05/29/dapr-an-introduction-to-the-runtime.aspx#feedback http://blogs.ugidotnet.org/martinobordin/comments/commentRss/102635.aspx http://blogs.ugidotnet.org/martinobordin/services/trackbacks/102635.aspx