mirror of https://github.com/abpframework/abp.git
6 changed files with 156 additions and 4 deletions
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 95 KiB |
@ -0,0 +1,152 @@ |
|||
# Microservice Tutorial Part 07: Integrating the services: Using Distributed Events |
|||
|
|||
````json |
|||
//[doc-nav] |
|||
{ |
|||
"Previous": { |
|||
"Name": "Integrating the services: HTTP API Calls", |
|||
"Path": "tutorials/microservice/part-06" |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Another common approach to communicating between microservices is messaging. By publishing and handling messages, a microservice can perform an operation when an event happens in another microservice. |
|||
|
|||
ABP provides two types of event buses for loosely coupled communication: |
|||
|
|||
* [Local Event Bus](../../framework/infrastructure/event-bus/local/index.md) is suitable for in-process messaging. However, it’s not suitable for microservices as it cannot communicate across different processes. For distributed systems, consider using a distributed event bus. |
|||
|
|||
* **[Distributed Event Bus](../../framework/infrastructure/event-bus/distributed/index.md)** is normal for inter-process messaging, like microservices, for publishing and subscribing to distributed events. However, ABP's distributed event bus works as local (in-process) by default (actually, it uses the Local Event Bus under the hood by default) unless you configure an external message broker. |
|||
|
|||
In this tutorial, we will use the distributed event bus to communicate between the `Order` and `Catalog` microservices. |
|||
|
|||
## Publishing an Event |
|||
|
|||
In the example scenario, we want to publish an event when a new order is placed. The Ordering service will publish the event since it knows when a new order is placed. The Catalog service will subscribe to that event and get notified when a new order is placed. This will decrease the stock count of the product related to the new order. The scenario is pretty simple; let's implement it. |
|||
|
|||
### Defining the Event Class |
|||
|
|||
Open the `CloudCrm.OrderingService` .NET solution in your IDE, create an `Events` folder and create a new class named `OrderPlacedEto` under the `CloudCrm.OrderingService.Contracts` project. |
|||
|
|||
```csharp |
|||
using System; |
|||
|
|||
namespace CloudCrm.OrderingService.Events; |
|||
|
|||
public class OrderPlacedEto |
|||
{ |
|||
public string CustomerName { get; set; } |
|||
public Guid ProductId { get; set; } |
|||
} |
|||
``` |
|||
|
|||
`OrderPlacedEto` is very simple. It is a plain C# class used to transfer data related to the event (*ETO* is an acronym for *Event Transfer Object*, a suggested naming convention but not required). You can add more properties if needed, but for this tutorial, it is more than enough. |
|||
|
|||
### Using the `IDistributedEventBus` Service |
|||
|
|||
The `IDistributedEventBus` service publishes events to the event bus. Until this point, the Ordering service only creates an Order and insert to database. Let's change that and publish `OrderPlacedEto` event, for that purpose open the `CloudCrm.OrderingService` project and update to the `OrderAppService` class as shown below: |
|||
|
|||
```csharp |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using CloudCrm.CatalogService.IntegrationServices; |
|||
using CloudCrm.OrderingService.Entities; |
|||
using CloudCrm.OrderingService.Enums; |
|||
using CloudCrm.OrderingService.Events; |
|||
using CloudCrm.OrderingService.Localization; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
|
|||
namespace CloudCrm.OrderingService.Services; |
|||
|
|||
public class OrderAppService : ApplicationService, IOrderAppService |
|||
{ |
|||
private readonly IRepository<Order> _orderRepository; |
|||
private readonly IProductIntegrationService _productIntegrationService; |
|||
private readonly IDistributedEventBus _distributedEventBus; |
|||
|
|||
public OrderAppService( |
|||
IRepository<Order, Guid> orderRepository, |
|||
IProductIntegrationService productIntegrationService, |
|||
IDistributedEventBus distributedEventBus) |
|||
{ |
|||
LocalizationResource = typeof(OrderingServiceResource); |
|||
|
|||
_orderRepository = orderRepository; |
|||
_productIntegrationService = productIntegrationService; |
|||
_distributedEventBus = distributedEventBus; |
|||
} |
|||
|
|||
public async Task<List<OrderDto>> GetListAsync() |
|||
{ |
|||
var orders = await _orderRepository.GetListAsync(); |
|||
|
|||
// Prepare a list of products we need |
|||
var productIds = orders.Select(o => o.ProductId).Distinct().ToList(); |
|||
var products = (await _productIntegrationService |
|||
.GetProductsByIdsAsync(productIds)) |
|||
.ToDictionary(p => p.Id, p => p.Name); |
|||
|
|||
var orderDtos = ObjectMapper.Map<List<Order>, List<OrderDto>>(orders); |
|||
|
|||
orderDtos.ForEach(orderDto => |
|||
{ |
|||
orderDto.ProductName = products[orderDto.ProductId]; |
|||
}); |
|||
|
|||
return orderDtos; |
|||
} |
|||
|
|||
public async Task CreateAsync(OrderCreationDto input) |
|||
{ |
|||
// Create a new Order entity |
|||
var order = new Order |
|||
{ |
|||
CustomerName = input.CustomerName, |
|||
ProductId = input.ProductId, |
|||
State = OrderState.Placed |
|||
}; |
|||
|
|||
// Save it to the database |
|||
await _orderRepository.InsertAsync(order); |
|||
|
|||
// Publish an event so other modules can be informed |
|||
await _distributedEventBus.PublishAsync( |
|||
new OrderPlacedEto |
|||
{ |
|||
ProductId = order.ProductId, |
|||
CustomerName = order.CustomerName |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
The `OrderAppService.CreateAsync` method creates a new `Order` entity, saves it to the database and finally publishes an `OrderPlacedEto` event. |
|||
|
|||
## Subscribing to an Event |
|||
|
|||
The Catalog service will subscribe to the `OrderPlacedEto` event and decrease the stock count of the product related to the new order. Let's implement it. |
|||
|
|||
### Adding a Reference to the `CloudCrm.OrderingService.Contracts` Package |
|||
|
|||
Since the `OrderPlacedEto` class is in the `CloudCrm.OrderingService.Contracts` project, we must add that package's reference to the Catalog service. This time, we will use the Import Module feature of ABP Studio (as an alternative to the approach we used in the Adding a Reference to the `CloudCrm.CatalogService.Contracts` Package section of the [previous part](./part-06.md#adding-a-reference-to-the-cloudcrmcatalogservicecontracts-package)). |
|||
|
|||
Open the ABP Studio UI and stop the applications if they are running. Then, open the *Solution Explorer* panel and right-click on the `CloudCrm.CatalogService`. Select *Import Module* from the context menu. |
|||
|
|||
 |
|||
|
|||
In the opening dialog, find and select the `CloudCrm.OrderingService` module, check the *Install this module* option click the *OK* button. |
|||
|
|||
 |
|||
|
|||
Once you click the OK button, the Ordering service is imported to the Catalog service. It opens the *Install Module* dialog. |
|||
|
|||
 |
|||
|
|||
Here, select the `CloudCrm.OrderingService.Contracts` package on the left side (because we want to add that package reference) and `CloudCrm.CatalogService` package on the middle area (because we want to add the package reference to that project). |
|||
|
|||
You can check the ABP Studio's *Solution Explorer* panel to see the module and the project reference (dependency). |
|||
|
|||
 |
|||
Loading…
Reference in new issue