diff --git a/src/Server/StellarChat.Server.Api/Program.cs b/src/Server/StellarChat.Server.Api/Program.cs
index 89dcb56..9de9188 100644
--- a/src/Server/StellarChat.Server.Api/Program.cs
+++ b/src/Server/StellarChat.Server.Api/Program.cs
@@ -1,6 +1,7 @@
 using StellarChat.Shared.Infrastructure.Exceptions;
 using StellarChat.Shared.Infrastructure.Contexts;
 using StellarChat.Shared.Infrastructure.Observability.Logging;
+using StellarChat.Shared.Infrastructure.DAL.Mongo;
 
 var builder = WebApplication.CreateBuilder(args);
 
@@ -11,6 +12,7 @@
 builder.Services.AddErrorHandling();
 builder.Services.AddContext();
 builder.Host.UseLogging();
+builder.Services.AddMongo(builder.Configuration);
 
 var app = builder.Build();
 
diff --git a/src/Shared/StellarChat.Shared.Abstractions/Pagination/IPagedQuery.cs b/src/Shared/StellarChat.Shared.Abstractions/Pagination/IPagedQuery.cs
new file mode 100644
index 0000000..50df3e9
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Abstractions/Pagination/IPagedQuery.cs
@@ -0,0 +1,9 @@
+namespace StellarChat.Shared.Abstractions.Pagination;
+
+public interface IPagedQuery
+{
+    int Page { get; set; }
+    int PageSize { get; set; }
+}
+
+public interface IPagedQuery<T> : IPagedQuery { }
diff --git a/src/Shared/StellarChat.Shared.Abstractions/Pagination/Paged.cs b/src/Shared/StellarChat.Shared.Abstractions/Pagination/Paged.cs
new file mode 100644
index 0000000..8ef6986
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Abstractions/Pagination/Paged.cs
@@ -0,0 +1,36 @@
+namespace StellarChat.Shared.Abstractions.Pagination;
+
+public class Paged<T> : PagedBase
+{
+    public IReadOnlyList<T> Items { get; set; } = Array.Empty<T>();
+    public bool Empty => Items is null || !Items.Any();
+    public bool HasNextPage => CurrentPage * ResultsPerPage < TotalResults;
+    public bool HasPreviousPage => CurrentPage > 1;
+
+    public Paged()
+    {
+        TotalPages = 1;
+    }
+
+    public Paged(IReadOnlyList<T> items,
+        int currentPage, int resultsPerPage,
+        int totalPages, long totalResults) :
+        base(currentPage, resultsPerPage, totalPages, totalResults)
+    {
+        Items = items;
+    }
+
+    public static Paged<T> Create(IReadOnlyList<T> items,
+        int currentPage, int resultsPerPage,
+        int totalPages, long totalResults)
+        => new(items, currentPage, resultsPerPage, totalPages, totalResults);
+
+    public static Paged<T> From(PagedBase result, IReadOnlyList<T> items)
+        => new(items, result.CurrentPage, result.ResultsPerPage,
+            result.TotalPages, result.TotalResults);
+
+    public static Paged<T> AsEmpty => new();
+
+    public Paged<TResult> Map<TResult>(Func<T, TResult> map)
+        => Paged<TResult>.From(this, Items.Select(map).ToList());
+}
diff --git a/src/Shared/StellarChat.Shared.Abstractions/Pagination/PagedBase.cs b/src/Shared/StellarChat.Shared.Abstractions/Pagination/PagedBase.cs
new file mode 100644
index 0000000..a340f99
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Abstractions/Pagination/PagedBase.cs
@@ -0,0 +1,20 @@
+namespace StellarChat.Shared.Abstractions.Pagination;
+
+public abstract class PagedBase
+{
+    public int CurrentPage { get; set; }
+    public int ResultsPerPage { get; set; }
+    public int TotalPages { get; set; }
+    public long TotalResults { get; set; }
+
+    protected PagedBase() { }
+
+    protected PagedBase(int currentPage, int resultsPerPage,
+        int totalPages, long totalResults)
+    {
+        CurrentPage = currentPage;
+        ResultsPerPage = resultsPerPage;
+        TotalPages = totalPages;
+        TotalResults = totalResults;
+    }
+}
diff --git a/src/Shared/StellarChat.Shared.Abstractions/Pagination/PagedQuery.cs b/src/Shared/StellarChat.Shared.Abstractions/Pagination/PagedQuery.cs
new file mode 100644
index 0000000..2895675
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Abstractions/Pagination/PagedQuery.cs
@@ -0,0 +1,11 @@
+namespace StellarChat.Shared.Abstractions.Pagination;
+
+public abstract class PagedQuery : IPagedQuery
+{
+    public int Page { get; set; } = 0;
+    public int PageSize { get; set; }
+    //public string OrderBy { get; set; } = string.Empty;
+    //public string SortOrder { get; set; } = string.Empty;
+}
+
+public abstract class PagedQuery<T> : PagedQuery, IPagedQuery<Paged<T>> { }
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Extensions.cs b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Extensions.cs
new file mode 100644
index 0000000..81a0eca
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Extensions.cs
@@ -0,0 +1,76 @@
+using Microsoft.Extensions.Configuration;
+using MongoDB.Bson.Serialization.Conventions;
+using MongoDB.Bson.Serialization.Serializers;
+using MongoDB.Bson.Serialization;
+using MongoDB.Bson;
+using MongoDB.Driver;
+using StellarChat.Shared.Infrastructure.DAL.Mongo.Factories;
+using StellarChat.Shared.Infrastructure.DAL.Mongo.Seeders;
+using Microsoft.Extensions.DependencyInjection;
+using StellarChat.Shared.Infrastructure.DAL.Mongo.Repositories;
+
+namespace StellarChat.Shared.Infrastructure.DAL.Mongo;
+
+public static class Extensions
+{
+    public static IServiceCollection AddMongo(this IServiceCollection services, IConfiguration configuration, Type? seederType = null)
+    {
+        var section = configuration.GetSection("mongo");
+        var options = section.BindOptions<MongoOptions>();
+        services.Configure<MongoOptions>(section);
+
+        if (!section.Exists())
+        {
+            return services;
+        }
+
+        var mongoClient = new MongoClient(options.ConnectionString);
+        var database = mongoClient.GetDatabase(options.Database);
+        services.AddSingleton<IMongoClient>(mongoClient);
+        services.AddSingleton(database);
+
+        services.AddTransient<IMongoSessionFactory, MongoSessionFactory>();
+
+        if (seederType is null)
+        {
+            services.AddTransient<IMongoDbSeeder, MongoDbSeeder>();
+
+            using var scope = services.BuildServiceProvider().CreateScope();
+            var seeder = scope.ServiceProvider.GetRequiredService<IMongoDbSeeder>();
+            seeder.SeedAsync(database).GetAwaiter().GetResult();
+        }
+        else
+        {
+            services.AddTransient(typeof(IMongoDbSeeder), seederType);
+        }
+
+        RegisterConventions();
+
+        return services;
+    }
+
+    public static IServiceCollection AddMongoRepository<TEntity, TIdentifiable>(this IServiceCollection services, string collectionName)
+        where TEntity : IIdentifiable<TIdentifiable>
+    {
+        services.AddTransient<IMongoRepository<TEntity, TIdentifiable>>(sp =>
+        {
+            var database = sp.GetRequiredService<IMongoDatabase>();
+            return new MongoRepository<TEntity, TIdentifiable>(database, collectionName);
+        });
+
+        return services;
+    }
+
+    private static void RegisterConventions()
+    {
+        BsonSerializer.RegisterSerializer(typeof(decimal), new DecimalSerializer(BsonType.Decimal128));
+        BsonSerializer.RegisterSerializer(typeof(decimal?),
+            new NullableSerializer<decimal>(new DecimalSerializer(BsonType.Decimal128)));
+        ConventionRegistry.Register("mongo-conventions", new ConventionPack
+        {
+            new CamelCaseElementNameConvention(),
+            new IgnoreExtraElementsConvention(true),
+            new EnumRepresentationConvention(BsonType.String),
+        }, _ => true);
+    }
+}
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Factories/MongoSessionFactory.cs b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Factories/MongoSessionFactory.cs
new file mode 100644
index 0000000..bc6009c
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Factories/MongoSessionFactory.cs
@@ -0,0 +1,14 @@
+using MongoDB.Driver;
+
+namespace StellarChat.Shared.Infrastructure.DAL.Mongo.Factories;
+
+internal sealed class MongoSessionFactory : IMongoSessionFactory
+{
+    private readonly IMongoClient _client;
+
+    public MongoSessionFactory(IMongoClient client)
+        => _client = client;
+
+    public Task<IClientSessionHandle> CreateAsync()
+        => _client.StartSessionAsync();
+}
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IAppSettingsSeeder.cs b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IAppSettingsSeeder.cs
new file mode 100644
index 0000000..9fb7772
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IAppSettingsSeeder.cs
@@ -0,0 +1,8 @@
+using MongoDB.Driver;
+
+namespace StellarChat.Shared.Infrastructure.DAL.Mongo;
+
+public interface IAppSettingsSeeder
+{
+    Task SeedAsync(IMongoDatabase database);
+}
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IIdentifiable.cs b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IIdentifiable.cs
new file mode 100644
index 0000000..df95a40
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IIdentifiable.cs
@@ -0,0 +1,6 @@
+namespace StellarChat.Shared.Infrastructure.DAL.Mongo;
+
+public interface IIdentifiable<out T>
+{
+    T Id { get; }
+}
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoDbSeeder.cs b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoDbSeeder.cs
new file mode 100644
index 0000000..f7aedf0
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoDbSeeder.cs
@@ -0,0 +1,9 @@
+using MongoDB.Driver;
+
+namespace StellarChat.Shared.Infrastructure.DAL.Mongo
+{
+    public interface IMongoDbSeeder
+    {
+        Task SeedAsync(IMongoDatabase database);
+    }
+}
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoRepository.cs b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoRepository.cs
new file mode 100644
index 0000000..c0530eb
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoRepository.cs
@@ -0,0 +1,24 @@
+using MongoDB.Driver;
+using StellarChat.Shared.Abstractions.Pagination;
+using System.Linq.Expressions;
+
+namespace StellarChat.Shared.Infrastructure.DAL.Mongo;
+
+public interface IMongoRepository<TDocument, in TIdentifiable> where TDocument : IIdentifiable<TIdentifiable>
+{
+    IMongoCollection<TDocument> Collection { get; }
+    IQueryable<TDocument> AsQueryable();
+    Task<TDocument> GetAsync(TIdentifiable id);
+    Task<TDocument> GetAsync(Expression<Func<TDocument, bool>> predicate);
+    Task<IReadOnlyList<TDocument>> FindAsync(Expression<Func<TDocument, bool>> predicate);
+    IEnumerable<TProjected> FilterBy<TProjected>(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, TProjected>> projectionExpression);
+    Task<Paged<TDocument>> BrowseAsync<TQuery>(Expression<Func<TDocument, bool>> predicate, TQuery query) where TQuery : IPagedQuery;
+    Task AddAsync(TDocument document);
+    Task AddManyAsync(ICollection<TDocument> documents);
+    Task UpdateAsync(TDocument document);
+    Task UpdateAsync(TDocument document, Expression<Func<TDocument, bool>> predicate);
+    Task DeleteAsync(TIdentifiable id);
+    Task DeleteAsync(Expression<Func<TDocument, bool>> predicate);
+    Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression);
+    Task<bool> ExistsAsync(Expression<Func<TDocument, bool>> predicate);
+}
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoSessionFactory.cs b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoSessionFactory.cs
new file mode 100644
index 0000000..8c5d877
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/IMongoSessionFactory.cs
@@ -0,0 +1,8 @@
+using MongoDB.Driver;
+
+namespace StellarChat.Shared.Infrastructure.DAL.Mongo;
+
+public interface IMongoSessionFactory
+{
+    Task<IClientSessionHandle> CreateAsync();
+}
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/MongoOptions.cs b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/MongoOptions.cs
new file mode 100644
index 0000000..095aa65
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/MongoOptions.cs
@@ -0,0 +1,8 @@
+namespace StellarChat.Shared.Infrastructure.DAL.Mongo;
+
+internal class MongoOptions
+{
+    public string ConnectionString { get; set; } = null!;
+    public string Database { get; set; } = null!;
+    public bool DisableTransactions { get; set; }
+}
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Pagination.cs b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Pagination.cs
new file mode 100644
index 0000000..2f8447b
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Pagination.cs
@@ -0,0 +1,56 @@
+using MongoDB.Driver;
+using MongoDB.Driver.Linq;
+using StellarChat.Shared.Abstractions.Pagination;
+using StellarChat.Shared.Infrastructure.DAL.Mongo;
+
+namespace StellarChat.Shared.Infrastructure.DAL.Mongo;
+
+public static class Pagination
+{
+    public static async Task<Paged<T>> PaginateAsync<T>(this IMongoQueryable<T> collection, IPagedQuery query)
+        => await collection.PaginateAsync(query.Page, query.PageSize);
+
+    public static async Task<Paged<T>> PaginateAsync<T>(this IMongoQueryable<T> collection,
+        int page, int resultsPerPage)
+    {
+        if (page <= 0)
+        {
+            page = 1;
+        }
+        if (resultsPerPage <= 0)
+        {
+            resultsPerPage = 10;
+        }
+        var isEmpty = await collection.AnyAsync() == false;
+        if (isEmpty)
+        {
+            return Paged<T>.AsEmpty;
+        }
+        var totalResults = await collection.CountAsync();
+        var totalPages = (int)Math.Ceiling((decimal)totalResults / resultsPerPage);
+        var data = await collection.Limit(page, resultsPerPage).ToListAsync();
+
+        return Paged<T>.Create(data, page, resultsPerPage, totalPages, totalResults);
+    }
+
+    public static IMongoQueryable<T> Limit<T>(this IMongoQueryable<T> collection, IPagedQuery query)
+        => collection.Limit(query.Page, query.PageSize);
+
+    public static IMongoQueryable<T> Limit<T>(this IMongoQueryable<T> collection,
+        int page, int resultsPerPage)
+    {
+        if (page <= 0)
+        {
+            page = 1;
+        }
+        if (resultsPerPage <= 0)
+        {
+            resultsPerPage = 10;
+        }
+        var skip = (page - 1) * resultsPerPage;
+        var data = collection.Skip(skip)
+            .Take(resultsPerPage);
+
+        return data;
+    }
+}
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Repositories/MongoRepository.cs b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Repositories/MongoRepository.cs
new file mode 100644
index 0000000..9c0d41e
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Repositories/MongoRepository.cs
@@ -0,0 +1,59 @@
+using System.Linq.Expressions;
+using MongoDB.Driver;
+using MongoDB.Driver.Linq;
+using StellarChat.Shared.Abstractions.Pagination;
+
+namespace StellarChat.Shared.Infrastructure.DAL.Mongo.Repositories;
+
+internal class MongoRepository<TDocument, TIdentifiable> : IMongoRepository<TDocument, TIdentifiable>
+        where TDocument : IIdentifiable<TIdentifiable>
+{
+    public MongoRepository(IMongoDatabase database, string collectionName)
+    {
+        Collection = database.GetCollection<TDocument>(collectionName);
+    }
+
+    public IMongoCollection<TDocument> Collection { get; }
+
+    public IQueryable<TDocument> AsQueryable() => Collection.AsQueryable();
+
+    public Task<TDocument> GetAsync(TIdentifiable id)
+        => GetAsync(e => e.Id.Equals(id));
+
+    public Task<TDocument> GetAsync(Expression<Func<TDocument, bool>> predicate)
+        => Collection.Find(predicate).SingleOrDefaultAsync();
+
+    public async Task<IReadOnlyList<TDocument>> FindAsync(Expression<Func<TDocument, bool>> predicate)
+        => await Collection.Find(predicate).ToListAsync();
+
+    public IEnumerable<TProjected> FilterBy<TProjected>(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, TProjected>> projectionExpression) 
+        => Collection.Find(filterExpression).Project(projectionExpression).ToEnumerable();
+
+    public Task<Paged<TDocument>> BrowseAsync<TQuery>(Expression<Func<TDocument, bool>> predicate,
+        TQuery query) where TQuery : IPagedQuery
+            => Collection.AsQueryable().Where(predicate).PaginateAsync(query);
+
+    public Task AddAsync(TDocument document)
+        => Collection.InsertOneAsync(document);
+
+    public async Task AddManyAsync(ICollection<TDocument> documents)
+        => await Collection.InsertManyAsync(documents);
+
+    public Task UpdateAsync(TDocument document)
+        => UpdateAsync(document, e => e.Id.Equals(document.Id));
+
+    public Task UpdateAsync(TDocument document, Expression<Func<TDocument, bool>> predicate)
+        => Collection.ReplaceOneAsync(predicate, document);
+
+    public Task DeleteAsync(TIdentifiable id)
+        => DeleteAsync(e => e.Id.Equals(id));
+
+    public Task DeleteAsync(Expression<Func<TDocument, bool>> predicate)
+        => Collection.DeleteOneAsync(predicate);
+
+    public Task<bool> ExistsAsync(Expression<Func<TDocument, bool>> predicate)
+        => Collection.Find(predicate).AnyAsync();
+
+    public async Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression) 
+        => await Collection.DeleteManyAsync(filterExpression);
+}
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Seeders/MongoDbSeeder.cs b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Seeders/MongoDbSeeder.cs
new file mode 100644
index 0000000..105ca75
--- /dev/null
+++ b/src/Shared/StellarChat.Shared.Infrastructure/DAL/Mongo/Seeders/MongoDbSeeder.cs
@@ -0,0 +1,30 @@
+using Microsoft.Extensions.Logging;
+using MongoDB.Driver;
+
+namespace StellarChat.Shared.Infrastructure.DAL.Mongo.Seeders;
+
+internal class MongoDbSeeder : IMongoDbSeeder
+{
+    private readonly IAppSettingsSeeder _appSettingsSeeder;
+    private readonly ILogger<MongoDbSeeder> _logger;
+
+    public MongoDbSeeder(IAppSettingsSeeder appSettingsSeeder, ILogger<MongoDbSeeder> logger)
+    {
+        _appSettingsSeeder = appSettingsSeeder;
+        _logger = logger;
+    }
+
+    public async Task SeedAsync(IMongoDatabase database)
+    {
+        await CustomSeedAsync(database);
+    }
+
+    protected virtual async Task CustomSeedAsync(IMongoDatabase database)
+    {
+        _logger.LogInformation("Started seeding the database.");
+
+        await _appSettingsSeeder.SeedAsync(database);
+
+        _logger.LogInformation("Finished seeding the database.");
+    }
+}
diff --git a/src/Shared/StellarChat.Shared.Infrastructure/StellarChat.Shared.Infrastructure.csproj b/src/Shared/StellarChat.Shared.Infrastructure/StellarChat.Shared.Infrastructure.csproj
index 10f0247..4071906 100644
--- a/src/Shared/StellarChat.Shared.Infrastructure/StellarChat.Shared.Infrastructure.csproj
+++ b/src/Shared/StellarChat.Shared.Infrastructure/StellarChat.Shared.Infrastructure.csproj
@@ -11,6 +11,7 @@
   </ItemGroup>  
 	
   <ItemGroup>
+	  <PackageReference Include="MongoDB.Driver" Version="2.24.0" />
 	  <PackageReference Include="Serilog" Version="4.0.0-dev-02113" />
 	  <PackageReference Include="Serilog.AspNetCore" Version="8.0.2-dev-00334" />
 	  <PackageReference Include="Serilog.Extensions.Logging" Version="8.0.1-dev-10377" />