# Commands, Events, and Messages Explained

> A brief comparison of messages, commands, and events in distributed systems as implemented by NServiceBus

- URL: https://blog.nimblepros.com/blogs/commands-events-messages-explained/
- Author: [Kyle McMaster](https://blog.nimblepros.com/authors/kyle/)
- Published: 2024-03-28
- Series: [NServiceBus](https://blog.nimblepros.com/series/nservicebus/)
- Categories: [Software Development](https://blog.nimblepros.com/categories/software-development/)
- Publisher: [NimblePros](https://blog.nimblepros.com/)

---

This post is the third in a series on [NServiceBus](https://go.particular.net/using-nservicebus-2024), a popular messaging framework for building distributed systems. In the [previous post](https://blog.nimblepros.com/blogs/getting-started-with-nservicebus/), we covered the basics of NServiceBus and how to set up a simple messaging system using the framework. In this post, we'll take a closer look at the concepts of messages, commands, and events in distributed systems.

When building distributed architectures, there are a few types of messages that can be sent between components in your system. Messages, commands, and events represent distinct types of objects that facilitate communication between different parts of a system. Understanding the difference between them is key to designing effective interactions and data flow within distributed architectures.

## What are Messages? 📨

First, it's important to understand that not every message sent in your application will be a command or an event. Messages are a more general term that encompasses commands, events, and simply data without intent or an associated action related to it. They are the units of communication that are passed between different parts of a system. A good example of a message that is neither a command nor an event is a request for information, such as a query to a database. These messages are not commands because they don't imply an action, and they are not events because they don't signal that something has happened. Instead, they are simply requests for information.

In NServiceBus, messages are simply the unit of communication that is passed between endpoints. These messages can be defined using the `IMessage` interface and communicated using the `Send` method on the `IMessageSession` or `IMessageHandlerContext` interfaces. They are then received by handlers that implement the `IHandleMessages<T>` interface, where `T` is the message type. The following is an idiomatic example of how messages are defined, sent, and received in NServiceBus:

```csharp
public class MyMessage : IMessage
{
  public string Content { get; init; }
}

public class MyMessageSender(IMessageSession _messageSession)
{
  public async Task SendMessage()
  {
    var message = new MyMessage
    {
      Content = "NServiceBus rocks!"
    };
    await _messageSession.Send(message);
  }
}

public class MyMessageHandler : IHandleMessages<MyMessage>
{
  public Task Handle(MyMessage message, IMessageHandlerContext context)
  {
    Console.WriteLine($"Received message with content: {message.Content}");
    return Task.CompletedTask;
  }
}
```

## What are Commands? 💪

Commands are messages that represent an intention to perform an action or to produce a side effect. They are imperative meaning they tell the recipient to do something. Commands are used to trigger a specific action in the receiving system, such as creating a new record in a database, updating a value, or sending a notification. Commands should typically be processed by one recipient. In NServiceBus, the `ICommand` interface is used to mark classes as commands. Commands in distributed systems don't return a value, but they can trigger messages that notify other parts of the system to satisfy any request/response requirements. Here is an example of how commands are defined and sent in NServiceBus.

```csharp
public class CreateContributorCommand : ICommand
{
  public string Name { get; init; }
  public string PhoneNumber { get; init; }
}

public class ContributorService(IMessageSession _messageSession)
{
  public async Task Create(string name, string phoneNumber)
  {
    var command = new CreateContributorCommand
    {
      Name = name,
      PhoneNumber = phoneNumber
    };
    await _messageSession.Send(command);
  }
}

public class CreateContributorCommandHandler(IRepository<Contributor> _repository)
  : IHandleMessages<CreateContributorCommand>
{
  public async Task Handle(CreateContributorCommand message, IMessageHandlerContext context)
  {
    var contributor = new Contributor(message.Name, message.PhoneNumber);
    await _repository.Add(contributor);
  }
}
```

In this code, we define a `CreateContributorCommand` that represents the intention to create a new contributor in the system. The command is sent from a [domain service](https://ardalis.com/domain-modeling-encapsulation/) and it is processed by a handler that creates a new contributor using a [Repository](https://deviq.com/design-patterns/repository-pattern).

## What are Events? 💥

Events are messages that represent something that has happened in the system. Events are typically used to notify other parts of the system that a particular state change has occurred, such as a new record being created, an update to an existing record, or a user logging in. These events represent changes that have already taken place. Events can be processed by multiple recipients. In NServiceBus, the `IEvent` interface is used to mark classes as events.

```csharp
public class ContributorCreatedEvent : IEvent
{
  public int Id { get; init; }
  public string Name { get; init; }
  public string PhoneNumber { get; init; }
}

public class CreateContributorCommandHandler(IRepository<Contributor> _repository)
  : IHandleMessages<CreateContributorCommand>
{
  public async Task Handle(CreateContributorCommand message, IMessageHandlerContext context)
  {
    // .. Create a new contributor in the system

    await context.Publish(new ContributorCreatedEvent
    {
      Id = contributor.Id,
      Name = message.Name,
      PhoneNumber = message.PhoneNumber
    });
  }
}

public class ContributorCreatedEventHandler(ILogger<ContributorCreatedEventHandler> _logger) 
  : IHandleMessages<ContributorCreatedEvent>
{
  public Task Handle(ContributorCreatedEvent message, IMessageHandlerContext context)
  {
    _logger.LogInformation("Received {EventName} for {ContributorId}",
      nameof(ContributorCreatedEvent),
      message.ContributorId);
    return Task.CompletedTask;
  }
}
```

Note that this is the `CreateContributorCommandHandler` from the previous section but we are adding the functionality of publishing an event. This is common in distributed systems when events are used to notify other parts of the system that a command has been processed. In this case, the `ContributorCreatedEvent` is published after a new contributor has been created in the system. If you've been following along with our NServiceBus content series, you'll recognize the `ContributorCreatedEventHandler` from the [previous post in this series](https://blog.nimblepros.com/blogs/getting-started-with-nservicebus/).

NServiceBus does enforce the distinction between commands and events which is important because it helps to clarify the intent of the message and how it should be processed by the receiving system. Commands are sent using the `Send` method, while events are published using the `Publish` method. For example, if you were to pass an `IEvent` using the `Send` method, an exception would be thrown with a message similar to "Best practice violation for message type 'ContributorCreatedEvent'. Events can have multiple recipients, so they should be published.". This helpful guidance towards best practices is one of the features of NServiceBus that I enjoy most.

## Conclusion 🎬

In this post, we've covered the basic definitions of messages, commands, and events in distributed systems. NServiceBus provides a simple and effective way to implement these patterns using the `IMessage`, `ICommand`, and `IEvent` interfaces. By following these conventions, you can build robust and reliable messaging systems that can scale with your application's needs. Be sure to check out the next post in this series on NServiceBus to learn more about asynchronous messaging patterns and how to implement them in your applications. 👋

## Further Reading 📚

- [NServiceBus Documentation](https://docs.particular.net/nservicebus/messaging/messages-events-commands#identifying-messages-marker-interfaces)
- [Azure Asynchronous messaging options](https://learn.microsoft.com/en-us/azure/architecture/guide/technology-choices/messaging)
