Skip to content

Event Consumers

Learn how to react to system events in nopCommerce using the event-driven architecture.

Overview

nopCommerce uses a publish-subscribe event model. Events are published when actions occur (orders placed, customers registered, etc.), and your plugin can subscribe to handle these events.

Creating an Event Consumer

Basic Consumer

csharp
using Nop.Core.Events;
using Nop.Services.Events;

public class OrderEventConsumer : IConsumer<OrderPlacedEvent>
{
    #region Fields

    private readonly ILogger<OrderEventConsumer> _logger;
    private readonly IMyService _myService;

    #endregion

    #region Ctor

    public OrderEventConsumer(
        ILogger<OrderEventConsumer> logger,
        IMyService myService)
    {
        _logger = logger;
        _myService = myService;
    }

    #endregion

    #region Methods

    public async Task HandleEventAsync(OrderPlacedEvent eventMessage)
    {
        var order = eventMessage.Order;
        
        _logger.LogInformation("Order {OrderId} placed for customer {CustomerId}", 
            order.Id, order.CustomerId);

        await _myService.ProcessNewOrderAsync(order);
    }

    #endregion
}

Registering the Consumer

Consumers are automatically discovered, but you can also register manually:

csharp
// Infrastructure/PluginStartup.cs
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
    services.AddScoped<IConsumer<OrderPlacedEvent>, OrderEventConsumer>();
}

Common Events

Order Events

EventTriggered When
OrderPlacedEventOrder is created
OrderPaidEventPayment is confirmed
OrderCancelledEventOrder is cancelled
OrderRefundedEventRefund processed
ShipmentSentEventShipment marked as sent
ShipmentDeliveredEventShipment delivered
csharp
public class OrderLifecycleConsumer : 
    IConsumer<OrderPlacedEvent>,
    IConsumer<OrderPaidEvent>,
    IConsumer<OrderCancelledEvent>
{
    public async Task HandleEventAsync(OrderPlacedEvent eventMessage)
    {
        // Handle new order
    }

    public async Task HandleEventAsync(OrderPaidEvent eventMessage)
    {
        // Handle payment confirmation
    }

    public async Task HandleEventAsync(OrderCancelledEvent eventMessage)
    {
        // Handle cancellation
    }
}

Customer Events

EventTriggered When
CustomerRegisteredEventNew customer registers
CustomerLoggedinEventCustomer logs in
CustomerActivatedEventAccount activated via email
csharp
public class CustomerEventConsumer : IConsumer<CustomerRegisteredEvent>
{
    private readonly IEmailService _emailService;

    public CustomerEventConsumer(IEmailService emailService)
    {
        _emailService = emailService;
    }

    public async Task HandleEventAsync(CustomerRegisteredEvent eventMessage)
    {
        var customer = eventMessage.Customer;
        
        // Send welcome email from your system
        await _emailService.SendWelcomeEmailAsync(customer.Email);
    }
}

Product Events

EventTriggered When
ProductSearchEventCustomer searches products
ProductViewedEventProduct page viewed
EntityInsertedEvent<Product>Product created
EntityUpdatedEvent<Product>Product updated
EntityDeletedEvent<Product>Product deleted

CRUD Events (Generic)

For any entity, you can subscribe to insert, update, and delete:

csharp
public class ProductChangeConsumer : 
    IConsumer<EntityInsertedEvent<Product>>,
    IConsumer<EntityUpdatedEvent<Product>>,
    IConsumer<EntityDeletedEvent<Product>>
{
    private readonly ICacheManager _cacheManager;

    public ProductChangeConsumer(ICacheManager cacheManager)
    {
        _cacheManager = cacheManager;
    }

    public async Task HandleEventAsync(EntityInsertedEvent<Product> eventMessage)
    {
        await _cacheManager.RemoveByPrefixAsync("MyPlugin.Products");
        // Sync to external system
    }

    public async Task HandleEventAsync(EntityUpdatedEvent<Product> eventMessage)
    {
        var product = eventMessage.Entity;
        await SyncToExternalSystemAsync(product);
    }

    public async Task HandleEventAsync(EntityDeletedEvent<Product> eventMessage)
    {
        var product = eventMessage.Entity;
        await RemoveFromExternalSystemAsync(product.Id);
    }
}

Publishing Custom Events

Define the Event

csharp
// Events/MyCustomEvent.cs
namespace Nop.Plugin.Widgets.MyPlugin.Events;

public class MyCustomEvent
{
    public MyCustomEvent(MyEntity entity, string action)
    {
        Entity = entity;
        Action = action;
    }

    public MyEntity Entity { get; }
    public string Action { get; }
}

Publish the Event

csharp
public class MyService : IMyService
{
    #region Fields

    private readonly IEventPublisher _eventPublisher;

    #endregion

    #region Ctor

    public MyService(IEventPublisher eventPublisher)
    {
        _eventPublisher = eventPublisher;
    }

    #endregion

    #region Methods

    public async Task ProcessAsync(MyEntity entity)
    {
        // Do processing...

        // Publish event for other consumers
        await _eventPublisher.PublishAsync(new MyCustomEvent(entity, "processed"));
    }

    #endregion
}

Consume Custom Events

csharp
public class MyCustomEventConsumer : IConsumer<MyCustomEvent>
{
    public async Task HandleEventAsync(MyCustomEvent eventMessage)
    {
        var entity = eventMessage.Entity;
        var action = eventMessage.Action;
        
        // Handle the event
    }
}

Best Practices

1. Keep Handlers Fast

csharp
// ❌ Don't block the main flow
public async Task HandleEventAsync(OrderPlacedEvent e)
{
    await SendToExternalApiAsync(e.Order); // Slow!
}

// ✅ Queue for background processing
public async Task HandleEventAsync(OrderPlacedEvent e)
{
    await _backgroundQueue.EnqueueAsync(e.Order.Id);
}

2. Handle Exceptions Gracefully

csharp
public async Task HandleEventAsync(OrderPlacedEvent eventMessage)
{
    try
    {
        await _myService.ProcessAsync(eventMessage.Order);
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Failed to process order {OrderId}", eventMessage.Order.Id);
        // Don't rethrow - don't break the order flow!
    }
}

3. Avoid Circular Events

csharp
// ❌ Can cause infinite loop
public async Task HandleEventAsync(EntityUpdatedEvent<Product> e)
{
    e.Entity.Description += " Updated!";
    await _productService.UpdateAsync(e.Entity); // Triggers another update event!
}

// ✅ Use flags or checks
public async Task HandleEventAsync(EntityUpdatedEvent<Product> e)
{
    if (e.Entity.Description.EndsWith(" Updated!"))
        return; // Already processed
    
    // Process...
}

4. Consider Event Order

Multiple consumers may handle the same event. Don't rely on execution order.

Next Steps

Released under the nopCommerce Public License.