Asynchronous Microservices Communication

We will create two microservices using asynchronous communication. When you request the microservice “ms-order” it will process your rules and then issue an event for the “ms-payment”.

Devprime-based applications natively incorporate an event-driven architecture receiving external events, process business rules by issuing internal events, and if necessary propagate events externally using the Stream adapter that connects natively with RabbitMQ, Kafka, and other platforms.

At that time we will put together a demonstration involving two Microservices. The first is “Order” to receive orders and the second “Payment” to process the payment. In the example we will use two containers created in Docker. It is essential to climb the MongoDB and RabbitMQ containers and configure add the queue “orderevents and paymentevents” in Rabbitmq. Before proceeding follow the initial steps installing DevPrime CLI.

If you already have MongoDB/RabbitMQ services, change the settings in the /src/App/app/appsettings.json file in State and Stream with your credentials.

Creating the first microservice “Order”

We will use the DevPrime CLI for the creation of microservices. In this example we will tell you the name of the new application, the stream service type as “RabbitMQ” for issuing asynchronous events, and state as “MongoDB” for data persistence. To start a new Microservice use the command sample displayed and a new application will be created in seconds and ready for production.
dp new Order --stream rabbitmq --state mongodb

Adding a marketplace business rule by speeding up development
dp marketplace order

Running accelerator for implementation of domain-based code. When you run the command the first time in this demo type “A” so that you can move forward and make the changes.
dp init

The accelerator automatically implements the classes of “Domain Events” in Domain, Event Handlers in Application, “Domain Services” in Application, “Application Services” in Application, Persistence in State, “API’s” in Web, and some unit tests in Tests as an example.

DevPrime CLI

By running the application again using the script “.\run.ps1” (Windows) or “.\run.sh” (Linux/macOS) we will already have a new preview of your API. Open the web browser at the http://localhost:5000 and find the post method in Swagger and click “Try it out”.

Fill in the JSON data and then click “execute”. Return to the commando prompt to track the result in the microservice log automatically generated by the DevPrime Observability adapter.

DevPrime CLI

To check out an example of code implemented by the accelerator, follow the OrderCreatedEventHandler code.cs in the src/Core/Application/EventHandlers/Order folder that provides a Handle where the state of an aggregate ‘order’ persists and then notifies via stream adapter.

public override dynamic Handle(OrderCreated orderCreated)
    {
        var success = false;
        var order = orderCreated.Get<Domain.Aggregates.Order.Order>();
        Dp.State.Order.Add(order);
        var destination = Dp.Settings.Default("stream.orderevents");
        var eventName = "OrderCreated";
        var eventData = new OrderCreatedEventDTO()
        {ID = order.ID, CustomerName = order.CustomerName, CustomerTaxID = order.CustomerTaxID, Total = order.Total};
        Dp.Stream.Send(destination, eventName, eventData);
        success = true;
        return success;
    }

DevPrime starting microservice

At this point the log demonstrates the receipt of an HTTP request in the “Order” API that passes it to the application service and then the business rule in the domain that processes and issues “OrderCreated” event that in addition to having the state persisted in the mongo resulted in a notification in the external world by the Stream.

By now entering RabbitMQ through the rabbitmq portal you can observe this event in the “OrderEvents” queue. This means that this microservice has already fulfilled its role and notified this business fact so that it can be perceived by another service.

Because the communication in this example is fully asynchronous if there are events in the RabbitMQ queue they will be read by the “Payment” microservice as soon as we set up. You can stop the application by pressing the “Control” + “C” key at any time and press again when you want.

Congratulations!!! You already have the microservice “Order”

Creating the second microservice “Payment”

The second microservices will call “Payment” and we will start creating it at that time. An additional item that we will do during the implementation is precisely to activate the “subscribe” in the Stream adapter so that we can receive the events issued by the microservice “Order”.

Running dp new and creating microservices
dp new ms-payment --state mongodb --stream rabbitmq

Adding a marketplace business rule by speeding up development
dp marketplace payment

Running accelerator for domain-based code implementation
dp init

Because we will run two microservices in the same local environment it is necessary to change the http port of Payment. In the ms-payment project folder edit the src/App/appsettings.json file in the web key and changing it to 5002 and 5003 as an example.

  "DevPrime_Web": {
    "url": "https://localhost:5003;http://localhost:5002",
    "enable": "true",
    "enableswagger": "true",
    "PostSuccess": "201",
    "PostFailure": "500",
    "GetSuccess": "200",
    "GetFailure": "500",
    "PatchSuccess": "200",
    "PatchFailure": "500",
    "PutSuccess": "200",
    "PutFailure": "500",
    "DeleteSuccess": "200",
    "DeleteFailure": "500"
  },

Run the application using the command below
.\run.ps1 ou ./run.sh (Linux, MacOS)

If the application rotated is running fine. Close it with “Control+C” and we follow the impleys to enable the receipt of events issued by the microservices “Order”.

The next step is to add the configuration in the Stream service and create a DTO to receive the event data. You can do the whole process manually or using the accelerator below.
dp add event OrderCreated -as PaymentService

If it requests change just confirm and follow and at the end we will have two files changed so that we can advance in the settings.

/src/Core/Application/Services/Payment/Model/OrderCreatedEventDTO.cs
/src/Adapters/Stream/EventStream.cs 

Open the File Edit the file “OrderCreatedEventDTO.cs” and src/Core/Application/Services/Payment/Model and add the properties below.
code src\Core\Application\Services\Payment\Model\OrderCreatedEventDTO.cs

1
2
3
4
5
6
    public class OrderCreatedEventDTO                     
      {                                                     
        public Guid OrderID { get; set; }
        public string CustomerName { get; set; }
        public double Value { get; set; }  
      }

Now edit the “EventStream” in the /src/Adapters/Stream/EventStream.cs and modify by adding the properties of our OrderCreatedEventDTO. We will modify the application service to use the “Add” service. This implementation indicates that we will be performing “Subscribe” in the event called “OrderCreated” that will be linked to the alias “Stream1” in the Stream adapter.
code src\Adapters\Stream\EventStream.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
namespace DevPrime.Stream;
public class EventStream : EventStreamBase, IEventStream
{
    public override void StreamEvents()
    {
        Subscribe<IPaymentService>("Stream1", "OrderCreated", (payload, paymentService, Dp) =>
        {
            var dto = System.Text.Json.JsonSerializer.Deserialize<OrderCreatedEventDTO>(payload);
            var command = new Payment()
            {
                CustomerName = dto.CustomerName,
                OrderID = dto.OrderID,
                Value = dto.Value
            };
            paymentService.Add(command);
        });
    }
}

The final step is to change the Stream Adapter configuration file in the file (src/App/appsettings.json) and add the queue/topic name “OrderEvents” in “subscribe” as example below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
    "DevPrime_Stream": [
        {
          "Alias": "Stream1",
          "Enable": "true",
          "Default": "true",
          "StreamType": "RabbitMQ",
          "HostName": "Localhost",
          "User": "guest",
          "Password": "guest",
          "Port": "5672",
          "Exchange": "devprime",
          "ExchangeType": "direct",
          "Retry": "3",
          "Fallback": "State1",
          "Subscribe": [ { "Queues": "orderevents" } ]
        }

Congratulations!!! You already have the microservices “Payment”

Asynchronous communication between microservices

Now you’ve smelled the big time running the two microservices. Open each in a window and run for those who have the services active. Go to the microservices url “Order” in http://localhost:5000 and then go to Post and click the “Try it out” button. Make a post and follow the incredible result with the complete detail in each Microservices and Payment reacting to the event received.

At this point we are checking the result of the processing in the microservices “Order” that receives
a post and issues the “OrderCreated” event. All details are automatically generated by the DevPrime Observability adapter.

[INF][Web]["https://localhost:5001;http://localhost:5000"][Host]["Production"][Parameters]["Appsettings"][RequestId: 3282e6f8-d2bd-4ccf-934a-546276a83038]
[2021-06-19T20:23:07.654-03:00][INF][Web]["HTTP"][Order][Add]["{\n  \"customerName\": \"Ramon\",\n  \"customerTaxID\": \"string\",\n  \"itens\": [\n    {\n      \"description\": \"string\",\n      \"amount\": 0,\n      \"sku\": \"string\",\n      \"price\": 0\n    }\n  ],\n  \"total\": 0\n}"][Origin:"https://localhost:5001/swagger/index.html"][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][Application][OrderService][Add][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][Domain][Order][Add][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][Domain][Order][Handle][Event]["OrderCreated"][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][Application][EventHandler]["OrderCreatedEventHandler"][Event]["OrderCreated"][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][State][Type "MongoDB"][Alias "State1"][OrderRepository][Add][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]
[INF][Stream][Type "RabbitMQ"][Alias "Stream1"][Out][Event]["OrderCreated"]["Delivered"]["OrderEvents"]["{\"SagaId\":\"00000000-0000-0000-0000-000000000000\",\"Id\":\"35bc8937-deb8-4e35-a8c4-003171fd6e1b\",\"CorrelationId\":\"06721dfc-fd50-4783-8ea7-df201c22599b\",\"AppId\":\"3282e6f8-d2bd-4ccf-934a-546276a83038\",\"AppName\":\"ms-order\",\"Version\":1,\"Name\":\"OrderCreated\",\"When\":\"2021-06-19T20:23:07.8540357-03:00\",\"Payload\":{\"CustomerName\":\"Ramon\",\"CustomerTaxID\":\"string\",\"Total\":0,\"ID\":\"55a72f16-f4e5-476b-85d9-e86c6e6f390c\"}}"][RequestId: 06721dfc-fd50-4783-8ea7-df201c22599b]

And now the moment the ‘OrderCreated’ event arrives in the ‘Payment’ microservices.

[INF][Web]["https://localhost:5003;http://localhost:5002"][Host]["Production"][Parameters]["Appsettings"][RequestId: 072b12e7-c941-4b4b-9823-cbd6e4425309]
[INF][Stream][Type "RabbitMQ"][Alias "Stream1"][In][Event]["OrderCreated"]["OrderEvents"]["{\"SagaId\":\"00000000-0000-0000-0000-000000000000\",\"Id\":\"35bc8937-deb8-4e35-a8c4-003171fd6e1b\",\"CorrelationId\":\"06721dfc-fd50-4783-8ea7-df201c22599b\",\"AppId\":\"3282e6f8-d2bd-4ccf-934a-546276a83038\",\"AppName\":\"ms-order\",\"Version\":1,\"Name\":\"OrderCreated\",\"When\":\"2021-06-19T20:23:07.8540357-03:00\",\"Payload\":{\"CustomerName\":\"Ramon\",\"CustomerTaxID\":\"string\",\"Total\":0,\"ID\":\"55a72f16-f4e5-476b-85d9-e86c6e6f390c\"}}"][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]
[INF][Application][PaymentService][Add][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]
[INF][Domain][Payment][Add][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]
[INF][Domain][Payment][Handle][Event]["PaymentCreated"][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]
[INF][Application][EventHandler]["PaymentCreatedEventHandler"][Event]["PaymentCreated"][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]
[INF][State][Type "MongoDB"][Alias "State1"][PaymentRepository][Add][RequestId: 35bc8937-deb8-4e35-a8c4-003171fd6e1b]

Congratulations🚀 You just developed two microservices. To use Kafka just change the stream adapter settings.

Last modified May 2, 2022 (9f8c9e9)