Part 2: Implementing a Sample System with Event Driven Domain using .Net 8

Fábio Salomão
4 min readJun 3, 2024

--

In the first part of this series, we introduced the fundamental concepts of Event Driven Domain architecture and its benefits. Now, let’s start implementing a sample system. This system will have two main services: the order service and the billing service, which will communicate through RabbitMQ.

Environment Setup

Before we start coding, we need to set up our development environment. We will use Visual Studio 2022, .Net 8, and RabbitMQ.

Installing RabbitMQ

  1. Docker: The easiest way to install RabbitMQ is using Docker. If you don’t have Docker installed yet, you can download it at docker.com.
  2. Running RabbitMQ: Run the following command to start a RabbitMQ container:
docker run -d --hostname my-rabbit --name some-rabbit -p 8080:15672 -p 5672:5672 rabbitmq:3-management

This command will start RabbitMQ and expose the management interface on port 15672. You can access it at http://localhost:15672 (username: guest, password: guest).

Observer and Pub/Sub Patterns

Before we move on to implementing the system, let’s understand two essential patterns for event-driven architecture: Observer and Publish/Subscribe (Pub/Sub).

Observer Pattern

The Observer pattern defines a one-to-many dependency between objects, where one object (the subject) notifies multiple objects (the observers) about state changes. This pattern is useful for scenarios where various parts of the system need to react to events generated by a central component.

Publish/Subscribe (Pub/Sub) Pattern

The Pub/Sub pattern expands the concept of the Observer, further decoupling event producers and consumers. Instead of observers registering directly with subjects, a messaging system (like RabbitMQ) acts as an intermediary. Producers (publishers) send messages to the messaging system, which then distributes these messages to interested consumers (subscribers).

Creating the .Net 8 Project

Let’s create a new .Net 8 project for our order service.

  1. Creating the Project: Open Visual Studio 2022 and create a new ASP.NET Core Web API project.
  2. Configuring the Project: Name the project OrderService and select .Net 8 as the framework version.
  3. Adding Dependencies: Add the following dependencies to your project:
dotnet add package RabbitMQ.Client
dotnet add package Microsoft.Extensions.Hosting

Implementing the Order Service

Let’s start by implementing the order service, which will be responsible for creating orders and publishing events to RabbitMQ.

Order Model

Create a new folder called Models and add the following Order class:

namespace OrderService.Models
{
public class Order
{
public Guid Id { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public DateTime CreatedAt { get; set; }
}
}

Event Publisher

Let’s create a service to publish events. Create a folder called Services and add the following interface and class:

namespace OrderService.Services
{
public interface IEventPublisher
{
void Publish<T>(T @event);
}
}
using RabbitMQ.Client;
using System.Text;
using System.Text.Json;

namespace OrderService.Services
{
public class RabbitMqEventPublisher : IEventPublisher
{
private readonly IConnection _connection;

public RabbitMqEventPublisher(IConnection connection)
{
_connection = connection;
}

public void Publish<T>(T @event)
{
using (var channel = _connection.CreateModel())
{
channel.QueueDeclare(queue: "orders", durable: false, exclusive: false, autoDelete: false, arguments: null);

var message = JsonSerializer.Serialize(@event);
var body = Encoding.UTF8.GetBytes(message);

channel.BasicPublish(exchange: "", routingKey: "orders", basicProperties: null, body: body);
}
}
}
}

Orders Controller

Create an OrdersController to handle order creation requests.

using Microsoft.AspNetCore.Mvc;
using OrderService.Models;
using OrderService.Services;

namespace OrderService.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IEventPublisher _eventPublisher;

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

[HttpPost]
public IActionResult CreateOrder([FromBody] Order order)
{
order.Id = Guid.NewGuid();
order.CreatedAt = DateTime.UtcNow;

// Publishing the event
_eventPublisher.Publish(order);

return Ok(order);
}
}
}

Configuring RabbitMQ in Startup

In the Program.cs file, configure the RabbitMQ connection and register the IEventPublisher:

using OrderService.Services;
using RabbitMQ.Client;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<IConnection>(sp =>
{
var factory = new ConnectionFactory() { HostName = "localhost" };
return factory.CreateConnection();
});

builder.Services.AddSingleton<IEventPublisher, RabbitMqEventPublisher>();

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Testing the Order Service

  1. Run the Project: Press F5 to start the project.
  2. Test the API: Use Postman or another tool to send a POST request to https://localhost:5001/api/orders with the following body:
{
"ProductName": "Sample Product",
"Quantity": 2,
"Price": 100.00
}

You should receive a response with the details of the created order and verify that the event was published to RabbitMQ.

In the next article, we will implement the billing service that will consume order events from RabbitMQ and generate corresponding invoices. If you have any feedback or something you’d like to add, feel free to comment!

--

--

Fábio Salomão

Developer since 2009 with experience in Microsoft .Net and Full Stack skills. React specialist, focused on creating innovative and efficient solutions.