Part 2: Implementing a Sample System with Event Driven Domain using .Net 8
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
- 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.
- 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.
- Creating the Project: Open Visual Studio 2022 and create a new ASP.NET Core Web API project.
- Configuring the Project: Name the project
OrderService
and select .Net 8 as the framework version. - 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
- Run the Project: Press F5 to start the project.
- 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!