Building Real-Time Chat Application with Clean Architecture in .NET Core using SignalR

Divyansh Bhatia
3 min readJul 8, 2023

Introduction

Real-time applications have become increasingly popular in today’s fast-paced world, requiring the ability to update data and communicate with clients in real-time. In this article, we will explore how to build scalable and maintainable real-time applications using the principles of Clean Architecture, focusing on a .NET Core SignalR WebSocket API.

Understanding Clean Architecture

Clean Architecture is a software design pattern that promotes separation of concerns and maintainability by dividing the application into distinct layers. These layers include the domain, application, and infrastructure layers, each with its own responsibilities. The core principle of Clean Architecture is the dependency rule, which states that the inner layers should have no knowledge of the outer layers.

Setting up the Project

To get started, let’s create a new .NET Core project and set up the necessary dependencies. We’ll be using the following libraries:

  • Microsoft.AspNetCore.SignalR: A real-time messaging library for .NET Core.
  • Microsoft.EntityFrameworkCore: An object-relational mapping framework for interacting with databases.

First, open Visual Studio and create a new ASP.NET Core Web Application project. Select the API template and choose .NET Core as the target framework. Next, install the required NuGet packages:

dotnet add package Microsoft.AspNetCore.SignalR
dotnet add package Microsoft.EntityFrameworkCore

Defining the Domain Model

In Clean Architecture, the domain layer represents the core business logic of the application. In our real-time application, let’s consider a chat application where users can send and receive messages. We will define the following domain model:

public class User
{
public int Id { get; set; }
public string Name { get; set; }
}

public class Message
{
public int Id { get; set; }
public string Content { get; set; }
public DateTime Timestamp { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}

Implementing the Application and Infrastructure Layers

The application layer contains the use cases and business logic of the application, while the infrastructure layer handles external concerns such as data access and communication.

Let’s create a ChatService class in the application layer, responsible for managing the chat functionality:

public class ChatService
{
private readonly IHubContext<ChatHub> _hubContext;
private readonly ApplicationDbContext _dbContext;

public ChatService(IHubContext<ChatHub> hubContext, ApplicationDbContext dbContext)
{
_hubContext = hubContext;
_dbContext = dbContext;
}

public async Task SendMessage(string userName, string messageContent)
{
var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.Name == userName);
if (user == null)
{
user = new User { Name = userName };
_dbContext.Users.Add(user);
await _dbContext.SaveChangesAsync();
}

var message = new Message
{
Content = messageContent,
Timestamp = DateTime.Now,
UserId = user.Id
};

_dbContext.Messages.Add(message);
await _dbContext.SaveChangesAsync();

await _hubContext.Clients.All.SendAsync("ReceiveMessage", message);
}
}

In the infrastructure layer, let’s create a SignalR hub named ChatHub to handle real-time communication with clients:

public class ChatHub : Hub
{
public async Task SendMessage(string userName, string messageContent)
{
var chatService = Context.GetHttpContext().RequestServices.GetService<ChatService>();
await chatService.SendMessage(userName, messageContent);
}
}

Configuring SignalR and Database Connection

To enable SignalR and configure the database connection, open the Startup.cs file and add the following code:

public void ConfigureServices(IServiceCollection services)
{
// Add SignalR
services.AddSignalR();

// Configure database connection
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Enable SignalR
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/chatHub");
});
}

Make sure to update the DefaultConnection connection string in the appsettings.json file to point to your database.

Building the Client Application

To consume the real-time functionality in the client application, let’s create a JavaScript file and include the following code:

const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.build();

connection.on("ReceiveMessage", function (message) {
// Handle received message
});

connection.start()
.then(function () {
// Connection established
})
.catch(function (error) {
console.error(error);
});

Conclusion

In this article, we explored how to build real-time applications using the principles of Clean Architecture and .NET Core SignalR. By following the separation of concerns and maintaining a clean and scalable architecture, we can develop robust and maintainable real-time applications.

--

--

Responses (3)