Asynchronous communication between microservices

We’ll create two microservices using asynchronous communication. When making the request in the ms-order microservice, it will process your rules and then emit an event for the ms-payment.

Devprime-based applications natively incorporate an ‘Event-Driven architecture’ approach by receiving external events, processing business rules by emitting internal events and if necessary propagating the events externally using the Stream adapter that natively connects with RabbitMQ, Kafka and other platforms.

At this point, we’ll set up a demo involving two microservices. The first is “Order” to receive orders and the second is “Payment” to process the payment. In the example, we’ll use two containers built in docker. It’s critical to upload the MongoDB and RabbitMQ containers and configure to add the queue “orderevents and paymentevents” in Rabbitmq. Before proceeding, follow the initial steps by installing the 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 “Order” microservice

We will use the Devprime CLI for creating the microservices. In this example, we’ll enter the name of the new application, the Stream service type as “RabbitMQ” for emitting asynchronous events, and the State as “MongoDB” for data persistence. To start a new microservice, use the command example shown 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 to Accelerate Development
dp marketplace order

Running accelerator for implementation of code based on the Domain. When you run the command the first time in this demo, type “A” so that you can go ahead 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

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

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

Devprime CLI

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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, which is passed on to the application service, and then the business rule in the domain that processes and emits the “OrderCreated” event, which, in addition to having the state persisted in mongo, resulted in a notification in the external world by Stream.

By now entering RabbitMQ through its portal, it is possible to 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 it up. You can stop the application by pressing the “Control” + “C” key at any time and load again whenever you want.

Congratulations !! You already have the “Order” microservice

Creating the second microservice “Payment”

The second microservices will be called “Payment” and we’ll start building it at that point. 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 emitted by the “Order” microservice.

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

Adding a Marketplace Business Rule to Accelerate Development
dp marketplace payment

Running Accelerator for Domain-Based Code Implementation
dp init

Since we’ll be running two microservices in the same on-premises environment, it’s 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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  "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 has run everything is fine. Close it with “Control+C” and we’ll continue with the implamentations to enable the reception of the events emitted by the “Order” microservices.

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

When you confirm the change, you will have three modified files so that we can move forward with the settings.

1
2
3
/src/Core/Application/Services/Payment/Model/OrderCreatedEventDTO.cs
/src/Adapters/Stream/EventStream.cs
/src/Adapters/Stream/GlobalUsings.cs

Open the file Edit the OrderCreatedEventDTO.cs file 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.cs” file by setting the properties set in the OrderCreatedEventDTO. We will modify the application service to use the “Add” service. This implementation indicates that we will be performing “Subscribe” on the event called “OrderCreated” that will be bound to the alias “Stream1” on 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, OrderCreatedEventDTO>("Stream1",
        "OrderCreated", (dto, paymentService, Dp) =>
        {
            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 per 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 “Payment” microservices

Asynchronous communication between microservices

Now you’ve reached the big time to run both microservices. Open each one in a window and run for those who have the services active. Go to the “Order” microservices url in http://localhost:5000 and then go to Post and click on 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.

Right now we are checking the result of the processing in the “Order” microservices that receive
a post and emits 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 comes the ‘OrderCreated’ event 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’ve just developed two microservices. To use Kafka, simply change the settings of the Stream adapter.

Last modified April 11, 2024 (cc33f7e6)