Skip to content

MongoDB in ConnectSoft Microservice Template

Purpose & Overview

MongoDB is a NoSQL document database that provides flexible, schema-less persistence for microservices. In the ConnectSoft Microservice Template, MongoDB serves as a document-based persistence option alongside NHibernate (SQL), enabling polyglot persistence strategies where different aggregates can use different storage technologies based on their requirements.

MongoDB integration provides:

  • Document-Based Storage: Store JSON-like documents with flexible schema
  • Horizontal Scalability: Built-in support for sharding and replication
  • High Performance: Optimized for read-heavy workloads and complex queries
  • Rich Query Language: Powerful querying with filters, projections, and aggregations
  • Schema Flexibility: Evolve document structure without migrations (though migrations are supported)
  • Repository Pattern: Consistent abstraction through repository interfaces
  • Specification Pattern: Type-safe query building with MongoDB filter builders
  • Unit of Work: Transaction management for multi-document operations (MongoDB 4.0+)

MongoDB Philosophy

MongoDB is ideal for document-heavy aggregates, content management, user profiles, product catalogs, and scenarios requiring schema flexibility. The template treats MongoDB as a first-class persistence option, providing the same repository and specification abstractions as SQL-based persistence, ensuring business logic remains persistence-agnostic.

Architecture Overview

MongoDB Position in Clean Architecture

Application Layer (DomainModel)
    ├── Processors (Commands/Writes)
    └── Retrievers (Queries/Reads)
    ↓ (Uses Repository Interfaces)
Infrastructure Layer (PersistenceModel.MongoDb)
    ├── Repositories (MongoDB Driver-based)
    ├── Specifications (MongoDB Filter Builder-based)
    ├── Mappings (BsonClassMap)
    └── Conventions (Guid serialization)
MongoDB Database
    ├── Collections (Documents)
    ├── Indexes
    └── Transactions (MongoDB 4.0+)

Project Structure

ConnectSoft.MicroserviceTemplate.PersistenceModel.MongoDb/
├── Repositories/
│   ├── MicroserviceAggregateRootsMongoDbRepository.cs
│   └── MicroserviceAggregateRootsMongoDbKeyedRepository.cs
├── Specifications/
│   ├── MicroserviceAggregateRootsMongoDbQueryableSpecification.cs
│   └── MicroserviceAggregateRootsMongoDbQueryableKeyedSpecification.cs
├── Mappings/
│   └── MicroserviceAggregateRootEntityMap.cs
├── Conventions/
│   └── MongoGuidMemberConvention.cs
└── MongoGuidConventions.cs

ConnectSoft.MicroserviceTemplate.DatabaseModel.MongoDb.Migrations/
└── MicroserviceMongoDbMigration.cs

Configuration

Service Registration

MongoDB is registered via extension methods:

// MicroserviceRegistrationExtensions.cs
#if UseMongoDb
    services.AddMongoDbPersistence(configuration);
#endif

#if Migrations && UseMongoDb
    services.AddMongoDbMigrator(configuration, typeof(MicroserviceMongoDbMigration));
#endif

#if (UseNHibernate || UseMongoDb)
    services.AddPersistenceModel();
#endif

Connection Configuration

MongoDB connection is configured in appsettings.json:

{
  "PersistenceModel": {
    "MongoDb": {
      "MongoDbConnectionStringKey": "ConnectSoft.MicroserviceTemplateMongoDb",
      "DatabaseName": "MICROSERVICE_DATABASE"
    }
  },
  "ConnectionStrings": {
    "ConnectSoft.MicroserviceTemplateMongoDb": "mongodb://localhost:27017"
  }
}

Configuration Options:

Option Type Default Description
MongoDbConnectionStringKey string "ConnectSoft.MicroserviceTemplateMongoDb" Key for connection string in ConnectionStrings section
DatabaseName string "MICROSERVICE_DATABASE" MongoDB database name

Connection String Formats

Local Development:

mongodb://localhost:27017

Replica Set:

mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=myReplicaSet

With Authentication:

mongodb://username:password@host:27017/database?authSource=admin

Connection String Options: - replicaSet: Replica set name - authSource: Authentication database - ssl: Enable SSL/TLS - readPreference: Read preference (primary, secondary, etc.) - maxPoolSize: Maximum connection pool size

MongoDB Registration Details

The AddMongoDbPersistence() extension method configures MongoDB:

// MongoDbExtensions.cs
internal static IServiceCollection AddMongoDbPersistence(
    this IServiceCollection services,
    IConfiguration configuration)
{
    ArgumentNullException.ThrowIfNull(services);
    ArgumentNullException.ThrowIfNull(configuration);

    // Register MongoDB client, database, and unit of work
    services.UseMongoDbPersistence(
        configuration: configuration,
        connectionStringKey: OptionsExtensions.PersistenceModelOptions.MongoDb.MongoDbConnectionStringKey,
        databaseName: OptionsExtensions.PersistenceModelOptions.MongoDb.DatabaseName,
        dependencyInjectionKey: MicroserviceConstants.MongoDbDIKey);

    // Register repositories (keyed or non-keyed)
    if (!string.IsNullOrEmpty(MicroserviceConstants.MongoDbDIKey))
    {
        services.AddKeyedScoped<IMicroserviceAggregateRootsRepository, MicroserviceAggregateRootsMongoDbKeyedRepository>(MicroserviceConstants.MongoDbDIKey);
        services.AddKeyedScoped<IMicroserviceAggregateRootsSpecification, MicroserviceAggregateRootsMongoDbQueryableKeyedSpecification>(MicroserviceConstants.MongoDbDIKey);
    }
    else
    {
        services.AddScoped<IMicroserviceAggregateRootsRepository, MicroserviceAggregateRootsMongoDbRepository>();
        services.AddScoped<IMicroserviceAggregateRootsSpecification, MicroserviceAggregateRootsMongoDbQueryableSpecification>();
    }

    // Register conventions and mappings
    MongoGuidConventions.EnsureRegistered();
    MicroserviceAggregateRootEntityMap.MapClasses();

    return services;
}

Class Mappings

Overview

MongoDB uses BSON (Binary JSON) serialization to map C# classes to MongoDB documents. The template uses convention-based mapping with explicit configuration for complex scenarios.

Automatic Mapping

MongoDB driver automatically maps classes using conventions:

  • Class Name → Collection Name: MicroserviceAggregateRootEntity"MicroserviceAggregateRoots" (pluralized)
  • Properties → Document Fields: ObjectId"ObjectId" (or "_id" if marked as ID)
  • Guid → BSON Binary: GUIDs are serialized as BSON binary with standard representation

Explicit Class Mapping

For explicit control, use BsonClassMap:

// MicroserviceAggregateRootEntityMap.cs
public static class MicroserviceAggregateRootEntityMap
{
    public static void MapClasses()
    {
        if (!BsonClassMap.IsClassMapRegistered(typeof(MicroserviceAggregateRootEntity)))
        {
            BsonClassMap.RegisterClassMap<MicroserviceAggregateRootEntity>(classMap =>
            {
                // Auto-map all properties
                classMap.AutoMap();
            });

            // Register interface serializer
            var testObjectSerializer = BsonSerializer.LookupSerializer<MicroserviceAggregateRootEntity>();
            BsonSerializer.RegisterSerializer(
                new ImpliedImplementationInterfaceSerializer<IMicroserviceAggregateRoot, MicroserviceAggregateRootEntity>(testObjectSerializer));
        }
    }
}

Manual Mapping Examples

Map Property to Different Field Name:

BsonClassMap.RegisterClassMap<MyEntity>(classMap =>
{
    classMap.AutoMap();
    classMap.MapMember(x => x.InternalId).SetElementName("_id");
    classMap.MapMember(x => x.CreatedDate).SetElementName("created_at");
});

Ignore Properties:

BsonClassMap.RegisterClassMap<MyEntity>(classMap =>
{
    classMap.AutoMap();
    classMap.UnmapMember(x => x.TemporaryProperty);
});

Map Collections:

BsonClassMap.RegisterClassMap<Order>(classMap =>
{
    classMap.AutoMap();
    classMap.MapMember(x => x.Items).SetElementName("order_items");
});

Attributes for Mapping

BsonId Attribute:

public class MyEntity
{
    [BsonId]
    public Guid Id { get; set; }
}

BsonElement Attribute:

public class MyEntity
{
    [BsonElement("created_at")]
    public DateTimeOffset CreatedAt { get; set; }
}

BsonIgnore Attribute:

public class MyEntity
{
    [BsonIgnore]
    public string ComputedProperty { get; set; }
}

BsonRepresentation Attribute:

public class MyEntity
{
    [BsonRepresentation(BsonType.String)]
    public Guid Id { get; set; } // Stored as string instead of binary
}

Conventions

Guid Serialization Convention

The template registers a convention to ensure GUIDs are serialized consistently:

// MongoGuidConventions.cs
public static class MongoGuidConventions
{
    public static void EnsureRegistered()
    {
        if (Interlocked.Exchange(ref initialized, 1) == 1)
        {
            return;
        }

        var pack = new ConventionPack { new MongoGuidMemberConvention() };
        ConventionRegistry.Register("ConnectSoft.Guid(Standard) Pack", pack, _ => true);
    }
}

// MongoGuidMemberConvention.cs
public sealed class MongoGuidMemberConvention : ConventionBase, IMemberMapConvention
{
    public void Apply(BsonMemberMap memberMap)
    {
        var t = memberMap.MemberType;

        if (t == typeof(Guid))
        {
            memberMap.SetSerializer(new GuidSerializer(GuidRepresentation.Standard));
        }
        else if (Nullable.GetUnderlyingType(t) == typeof(Guid))
        {
            memberMap.SetSerializer(new NullableSerializer<Guid>(new GuidSerializer(GuidRepresentation.Standard)));
        }
    }
}

Benefits: - Consistent GUID serialization across all entities - Standard representation for interoperability - Applied automatically to all Guid and Guid? properties

Collections

Collection Naming

Collections are named automatically based on entity type:

// Automatic collection name: "MicroserviceAggregateRoots"
var collection = database.GetCollection<MicroserviceAggregateRootEntity>("MicroserviceAggregateRoots");

Collection Name Convention: - Entity class name pluralized - Example: MicroserviceAggregateRootEntity"MicroserviceAggregateRoots"

Custom Collection Names

Using Attribute:

[BsonCollection("CustomCollectionName")]
public class MyEntity
{
    // ...
}

In Mapping:

BsonClassMap.RegisterClassMap<MyEntity>(classMap =>
{
    classMap.SetCollectionName("custom_collection_name");
    classMap.AutoMap();
});

Collection Creation

Collections are created automatically on first insert, or explicitly via migrations:

// Via migration
[Migration(0, "Create collections")]
public class CreateCollectionsMigration : Migration
{
    public override void Up()
    {
        this.Database.CreateCollection("MicroserviceAggregateRoots");
    }
}

Repositories

MongoDB Repository Implementation

Repositories inherit from MongoDbRepository<TEntity, TIdentity>:

// MicroserviceAggregateRootsMongoDbRepository.cs
public class MicroserviceAggregateRootsMongoDbRepository(
    IUnitOfWork unitOfWork,
    IMongoDatabase mongoDatabase,
    ISpecificationLocator specificationLocator,
    ILogger<MongoDbRepository<IMicroserviceAggregateRoot, Guid>> logger)
    : MongoDbRepository<IMicroserviceAggregateRoot, Guid>(unitOfWork, mongoDatabase, specificationLocator, logger), 
      IMicroserviceAggregateRootsRepository
{
}

Base Class Provides: - CRUD operations (Insert, Update, Delete, GetById, GetAll) - Query operations (Query, QueryAsync) - Specification pattern support - Unit of Work integration

Repository Operations

Insert:

var entity = new MicroserviceAggregateRootEntity { ObjectId = Guid.NewGuid() };
await repository.InsertAsync(entity, cancellationToken);

Update:

entity.SomeValue = "Updated";
await repository.UpdateAsync(entity, cancellationToken);

Delete:

await repository.DeleteAsync(entity, cancellationToken);
// Or by ID
await repository.DeleteAsync(id, cancellationToken);

Get by ID:

var entity = await repository.GetByIdAsync(id, cancellationToken);

Get All:

var allEntities = await repository.GetAllAsync(cancellationToken);

Query:

var results = await repository.QueryAsync(
    x => x.SomeValue == "test",
    cancellationToken);

Keyed Repositories

For multi-database scenarios, use keyed repositories:

// MicroserviceAggregateRootsMongoDbKeyedRepository.cs
public class MicroserviceAggregateRootsMongoDbKeyedRepository(
    [FromKeyedServices(MicroserviceConstants.MongoDbDIKey)] IUnitOfWork unitOfWork,
    [FromKeyedServices(MicroserviceConstants.MongoDbDIKey)] IMongoDatabase mongoDatabase,
    [FromKeyedServices(MicroserviceConstants.MongoDbDIKey)] ISpecificationLocator specificationLocator,
    [FromKeyedServices(MicroserviceConstants.MongoDbDIKey)] ILogger<MongoDbRepository<IMicroserviceAggregateRoot, Guid>> logger)
    : MongoDbRepository<IMicroserviceAggregateRoot, Guid>(unitOfWork, mongoDatabase, specificationLocator, logger), 
      IMicroserviceAggregateRootsRepository
{
}

Usage with Keyed Services:

public class MyService
{
    private readonly IMicroserviceAggregateRootsRepository mongoRepository;

    public MyService(
        [FromKeyedServices(MicroserviceConstants.MongoDbDIKey)] 
        IMicroserviceAggregateRootsRepository mongoRepository)
    {
        this.mongoRepository = mongoRepository;
    }
}

Specifications and Queries

MongoDB Queryable Specification

Specifications translate to MongoDB filter builders:

// MicroserviceAggregateRootsMongoDbQueryableSpecification.cs
public class MicroserviceAggregateRootsMongoDbQueryableSpecification(
    IMongoDatabase mongoDatabase, 
    ILogger<MongoDbQueryableSpecification<IMicroserviceAggregateRoot, Guid>> logger)
    : MongoDbQueryableSpecification<IMicroserviceAggregateRoot, Guid>(mongoDatabase, logger), 
      IMicroserviceAggregateRootsSpecification
{
}

Using Specifications

// In retriever
public async Task<IReadOnlyList<IMicroserviceAggregateRoot>> GetActiveAggregatesAsync(
    CancellationToken ct)
{
    var specification = this.repository.Specify<IMicroserviceAggregateRootsSpecification>()
        .Where(x => x.SomeValue == "Active")
        .OrderByDescending(x => x.CreatedOn);

    return await specification.ToListAsync(ct);
}

Direct MongoDB Queries

For complex queries, access MongoDB directly:

public class MyService
{
    private readonly IMongoDatabase mongoDatabase;

    public MyService(IMongoDatabase mongoDatabase)
    {
        this.mongoDatabase = mongoDatabase;
    }

    public async Task<List<MyEntity>> ComplexQueryAsync()
    {
        var collection = mongoDatabase.GetCollection<MyEntity>("MyEntities");

        var filter = Builders<MyEntity>.Filter.And(
            Builders<MyEntity>.Filter.Eq(x => x.Status, "Active"),
            Builders<MyEntity>.Filter.Gte(x => x.CreatedOn, DateTimeOffset.UtcNow.AddDays(-30))
        );

        var sort = Builders<MyEntity>.Sort.Descending(x => x.CreatedOn);

        return await collection
            .Find(filter)
            .Sort(sort)
            .Limit(100)
            .ToListAsync();
    }
}

Aggregation Pipelines

For complex aggregations:

var pipeline = new BsonDocument[]
{
    new BsonDocument("$match", new BsonDocument("Status", "Active")),
    new BsonDocument("$group", new BsonDocument
    {
        { "_id", "$Category" },
        { "count", new BsonDocument("$sum", 1) }
    }),
    new BsonDocument("$sort", new BsonDocument("count", -1))
};

var results = await collection.Aggregate<BsonDocument>(pipeline).ToListAsync();

Indexes

Creating Indexes

Indexes are created via migrations:

[Migration(1, "Create indexes")]
public class CreateIndexesMigration : Migration
{
    public override void Up()
    {
        var collection = this.Database.GetCollection<BsonDocument>("MicroserviceAggregateRoots");

        // Single field index
        var singleFieldIndex = new CreateIndexModel<BsonDocument>(
            Builders<BsonDocument>.IndexKeys.Ascending("ObjectId"),
            new CreateIndexOptions { Unique = true });

        collection.Indexes.CreateOne(singleFieldIndex);

        // Compound index
        var compoundIndex = new CreateIndexModel<BsonDocument>(
            Builders<BsonDocument>.IndexKeys
                .Ascending("Status")
                .Descending("CreatedOn"),
            new CreateIndexOptions { Unique = false });

        collection.Indexes.CreateOne(compoundIndex);
    }
}

Index Types

Single Field Index:

var index = new CreateIndexModel<BsonDocument>(
    Builders<BsonDocument>.IndexKeys.Ascending("Status"));

Compound Index:

var index = new CreateIndexModel<BsonDocument>(
    Builders<BsonDocument>.IndexKeys
        .Ascending("Status")
        .Descending("CreatedOn"));

Text Index:

var index = new CreateIndexModel<BsonDocument>(
    Builders<BsonDocument>.IndexKeys.Text("Name").Text("Description"));

Geospatial Index:

var index = new CreateIndexModel<BsonDocument>(
    Builders<BsonDocument>.IndexKeys.Geo2DSphere("Location"));

TTL Index:

var index = new CreateIndexModel<BsonDocument>(
    Builders<BsonDocument>.IndexKeys.Ascending("ExpiresAt"),
    new CreateIndexOptions { ExpireAfter = TimeSpan.Zero });

Index Options

var index = new CreateIndexModel<BsonDocument>(
    Builders<BsonDocument>.IndexKeys.Ascending("Email"),
    new CreateIndexOptions
    {
        Unique = true,
        Sparse = true,
        Background = false,
        Name = "IX_Email_Unique"
    });

Migrations

MongoDB Migrations Overview

MongoDB migrations use CSharpMongoMigrations to manage collection creation, indexes, and schema evolution.

See Database Migrations for comprehensive documentation.

Migration Example

// MicroserviceMongoDbMigration.cs
[Migration(0, "First ConnectSoft.MicroserviceTemplate's MongoDb migration")]
public class MicroserviceMongoDbMigration : Migration
{
    public override void Up()
    {
        this.Database.CreateCollection("MicroserviceAggregateRoots");
    }

    public override void Down()
    {
        this.Database.DropCollection("MicroserviceAggregateRoots");
    }
}

Migration Registration

// MongoDbExtensions.cs
#if Migrations
internal static IServiceCollection AddMongoDbMigrator(
    this IServiceCollection services,
    IConfiguration configuration,
    Type assemblyType)
{
    services.UseMongoDbMigrator(
        configuration: configuration,
        connectionStringKey: OptionsExtensions.PersistenceModelOptions.MongoDb.MongoDbConnectionStringKey,
        databaseName: OptionsExtensions.PersistenceModelOptions.MongoDb.DatabaseName,
        assemblyType: assemblyType);

    return services;
}
#endif

Migrations run automatically and synchronously during service registration when UseMongoDbMigrator() is called. The LoggingMigrationRunner is used internally to provide structured logging and OpenTelemetry tracing for migration operations.

Unit of Work Pattern

Transaction Management

MongoDB supports multi-document transactions (MongoDB 4.0+). The Unit of Work pattern provides transaction management:

public class DefaultMicroserviceAggregateRootsProcessor : IMicroserviceAggregateRootsProcessor
{
    private readonly IUnitOfWork unitOfWork;
    private readonly IMicroserviceAggregateRootsRepository repository;

    public async Task<IMicroserviceAggregateRoot> CreateMicroserviceAggregateRoot(
        CreateMicroserviceAggregateRootInput input,
        CancellationToken token = default)
    {
        using var transaction = await this.unitOfWork.BeginTransactionAsync(token);

        try
        {
            var aggregate = new MicroserviceAggregateRootEntity
            {
                ObjectId = input.ObjectId,
                SomeValue = input.SomeValue
            };

            await this.repository.InsertAsync(aggregate, token);
            await transaction.CommitAsync(token);

            return aggregate;
        }
        catch
        {
            // Transaction rolls back automatically on disposal if not committed
            throw;
        }
    }
}

Transaction Requirements: - MongoDB 4.0+ (replica set or sharded cluster) - Replica set for transactions (not standalone) - WiredTiger storage engine

Health Checks

MongoDB Health Check

MongoDB health check verifies database connectivity:

// HealthChecksExtensions.cs
#if UseMongoDb
builder.AddMongoDb(
    mongoDatabase: serviceProvider =>
    {
        if (!string.IsNullOrEmpty(MicroserviceConstants.MongoDbDIKey))
        {
            return serviceProvider.GetRequiredKeyedService<IMongoClient>(MicroserviceConstants.MongoDbDIKey)
                .GetDatabase(OptionsExtensions.PersistenceModelOptions.MongoDb.DatabaseName);
        }
        else
        {
            return serviceProvider.GetRequiredService<IMongoClient>()
                .GetDatabase(OptionsExtensions.PersistenceModelOptions.MongoDb.DatabaseName);
        }
    },
    name: "ConnectSoft.MicroserviceTemplate MongoDb persistence health check",
    tags: new string[] { "application mongodb", "ready" });
#endif

Health Check Endpoint: - /health/ready: Includes MongoDB check - Returns Healthy if MongoDB is accessible - Returns Unhealthy if connection fails

Observability

OpenTelemetry Integration

MongoDB operations are automatically traced via OpenTelemetry:

// OpenTelemetryExtensions.cs
#if UseMongoDb
.AddSource("MongoDB.Driver.Core.Extensions.DiagnosticSources")
#endif

Traced Operations: - Database commands - Query execution - Insert/Update/Delete operations - Connection pool operations

Logging

MongoDB driver logs are integrated with application logging:

// Configure MongoDB logging
services.AddSingleton<ILogger<MongoClient>>(serviceProvider =>
    serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<MongoClient>());

Best Practices

Do's

  1. Use Repository Pattern

    // ✅ GOOD - Use repository abstraction
    await repository.InsertAsync(entity, cancellationToken);
    
    // ❌ BAD - Direct MongoDB access in business logic
    await collection.InsertOneAsync(entity);
    

  2. Create Indexes for Query Performance

    // ✅ GOOD - Index frequently queried fields
    var index = new CreateIndexModel<BsonDocument>(
        Builders<BsonDocument>.IndexKeys.Ascending("Status"));
    

  3. Use Specifications for Complex Queries

    // ✅ GOOD - Type-safe specification
    var spec = repository.Specify<IMySpecification>()
        .Where(x => x.Status == "Active");
    

  4. Handle Transactions Properly

    // ✅ GOOD - Explicit transaction management
    using var transaction = await unitOfWork.BeginTransactionAsync(token);
    try
    {
        await repository.InsertAsync(entity, token);
        await transaction.CommitAsync(token);
    }
    catch
    {
        throw; // Automatic rollback
    }
    

  5. Use Appropriate Collection Names

    // ✅ GOOD - Descriptive, plural names
    "MicroserviceAggregateRoots"
    
    // ❌ BAD - Abbreviated or unclear
    "MARs"
    

  6. Register Conventions Early

    // ✅ GOOD - Register before mapping
    MongoGuidConventions.EnsureRegistered();
    MicroserviceAggregateRootEntityMap.MapClasses();
    

Don'ts

  1. Don't Mix Direct MongoDB Access with Repository Pattern

    // ❌ BAD - Mixed patterns
    await repository.InsertAsync(entity1, token);
    await collection.InsertOneAsync(entity2); // Inconsistent
    

  2. Don't Create Indexes on Every Insert

    // ❌ BAD - Expensive operation
    await collection.Indexes.CreateOneAsync(index); // In hot path
    
    // ✅ GOOD - Create indexes in migrations
    

  3. Don't Ignore Transaction Requirements

    // ❌ BAD - Assumes transactions work everywhere
    using var transaction = await unitOfWork.BeginTransactionAsync(token);
    // Fails on standalone MongoDB
    

  4. Don't Use Large Document Sizes

    // ❌ BAD - Documents > 16MB (MongoDB limit)
    // Split into multiple documents or use GridFS
    

  5. Don't Skip Index Creation

    // ❌ BAD - No indexes on queried fields
    // Leads to collection scans
    

Troubleshooting

Issue: Connection Failures

Symptoms: Cannot connect to MongoDB instance.

Solutions: 1. Verify connection string format 2. Check MongoDB server is running 3. Verify network connectivity (firewall, ports) 4. Check authentication credentials 5. Verify replica set configuration (if using transactions)

Issue: Collection Not Found

Symptoms: MongoCommandException with "collection not found".

Solutions: 1. Create collection via migration 2. Collections are auto-created on first insert (if not disabled) 3. Verify collection name matches mapping

Issue: Transaction Errors

Symptoms: Transaction operations fail with "transaction not supported".

Solutions: 1. Verify MongoDB version is 4.0+ 2. Ensure replica set (not standalone) for transactions 3. Check WiredTiger storage engine 4. Verify transaction is properly committed or rolled back

Issue: Performance Issues

Symptoms: Slow queries, high CPU usage.

Solutions: 1. Create Indexes: Add indexes for frequently queried fields 2. Use Projections: Select only needed fields 3. Limit Results: Use Limit() and Skip() for pagination 4. Optimize Queries: Use Explain() to analyze query plans 5. Review Index Usage: Check if indexes are being used

Issue: Serialization Errors

Symptoms: BsonSerializationException during serialization.

Solutions: 1. Verify class mapping is registered 2. Check property types are supported 3. Ensure conventions are registered before mapping 4. Verify BsonId attribute on ID property 5. Check for circular references (use [BsonIgnore])

Issue: Index Creation Failures

Symptoms: Index creation fails in migrations.

Solutions: 1. Verify index definition syntax 2. Check for duplicate index names 3. Ensure sufficient disk space 4. Verify index options are valid 5. Check index name length (MongoDB limit)

Summary

MongoDB in the ConnectSoft Microservice Template provides:

  • Document-Based Storage: Flexible schema for document-heavy aggregates
  • Repository Pattern: Consistent abstraction matching SQL persistence
  • Specification Pattern: Type-safe query building with MongoDB filters
  • Class Mappings: Automatic mapping with explicit configuration support
  • Conventions: Consistent GUID serialization and other conventions
  • Migrations: Versioned collection and index management
  • Transactions: Multi-document transaction support (MongoDB 4.0+)
  • Health Checks: Built-in connectivity verification
  • Observability: OpenTelemetry tracing for all operations
  • Polyglot Persistence: Use alongside NHibernate for different aggregates

By following these patterns, teams can:

  • Store Documents Efficiently: Leverage MongoDB's document model for flexible data structures
  • Maintain Consistency: Use repository pattern for persistence-agnostic business logic
  • Query Effectively: Use specifications and indexes for performant queries
  • Scale Horizontally: Leverage MongoDB's built-in sharding and replication
  • Monitor Operations: Integrate with observability infrastructure for full visibility

MongoDB integration ensures that microservices can efficiently store and query document-based data while maintaining consistency with the template's architectural patterns and best practices.