Skip to content

Troubleshooting Momentum Applications

This guide covers common issues you might encounter when building applications with Momentum and how to resolve them.

Common Startup Issues

"Assembly not found" Errors

Symptoms:

  • Application fails to start
  • Error messages about missing assemblies
  • Commands/queries not being discovered

Solutions:

  1. Mark domain assemblies correctly:
csharp
// In your API project Program.cs
using YourApp.Domain;

[assembly: DomainAssembly(typeof(IYourDomainAssembly))]

var builder = WebApplication.CreateSlimBuilder(args);
// ... rest of configuration
  1. Create proper domain marker interfaces:
csharp
// YourApp.Domain/IYourDomainAssembly.cs
namespace YourApp.Domain;

public interface IYourDomainAssembly;
  1. Set entry assembly manually if needed:
csharp
// Before calling AddServiceDefaults()
ServiceDefaultsExtensions.EntryAssembly = typeof(Program).Assembly;

builder.AddServiceDefaults();

Service Discovery Issues

Symptoms:

  • Services not being registered
  • Dependency injection failures
  • "Service not found" exceptions

Solutions:

  1. Verify service defaults are added:
csharp
var builder = WebApplication.CreateSlimBuilder(args);

// This must be called first
builder.AddServiceDefaults();

// Then add your custom services
builder.Services.AddScoped<IMyService, MyService>();
  1. Check domain assembly registration:
csharp
// Verify assemblies are marked correctly
[assembly: DomainAssembly(typeof(IDomainMarker))]

// Check logs for assembly discovery
logger.LogInformation("Discovered assemblies: {Assemblies}",
    DomainAssemblyAttribute.GetDomainAssemblies().Select(a => a.FullName));
  1. Manual service registration as fallback:
csharp
// If automatic discovery fails
builder.Services.AddValidatorsFromAssemblyContaining<MyValidator>();
builder.Services.AddMediatR(typeof(MyCommand));

Database Connection Issues

Connection String Problems

Symptoms:

  • "Cannot connect to database" errors
  • Timeout exceptions
  • Authentication failures

Solutions:

  1. Verify connection string format:
json
{
    "ConnectionStrings": {
        "DefaultConnection": "Host=localhost;Port=5432;Database=myapp;Username=postgres;Password=password"
    }
}
  1. Check database availability:
bash
# Test PostgreSQL connection
psql -h localhost -p 5432 -U postgres -d myapp

# Check if database exists
SELECT datname FROM pg_database WHERE datname = 'myapp';
  1. Use connection string validation:
csharp
public static class DatabaseExtensions
{
    public static void ValidateConnectionString(this IConfiguration configuration)
    {
        var connectionString = configuration.GetConnectionString("DefaultConnection");

        if (string.IsNullOrEmpty(connectionString))
        {
            throw new InvalidOperationException("Database connection string not found");
        }

        try
        {
            using var connection = new NpgsqlConnection(connectionString);
            connection.Open();
            connection.Close();
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException($"Cannot connect to database: {ex.Message}", ex);
        }
    }
}

// In Program.cs
builder.Configuration.ValidateConnectionString();

Migration Issues

Symptoms:

  • Database schema not up to date
  • Missing tables or columns
  • Migration execution failures

Solutions:

  1. Run migrations manually:
bash
# Using Docker Compose
docker compose up myapp-db-migrations

# Or check migration status
docker compose exec myapp-db-migrations liquibase status
  1. Verify migration files:
bash
# Check migration directory
ls -la infra/MyApp.Database/migrations/

# Validate Liquibase changelog
docker compose exec myapp-db-migrations liquibase validate
  1. Reset database for development:
bash
# Completely reset development database
docker compose down -v
docker compose up myapp-db myapp-db-migrations

Validation Issues

Validators Not Found

Symptoms:

  • Validation not running
  • Commands/queries not being validated
  • No validation errors when expected

Solutions:

  1. Check validator registration:
csharp
// Ensure validators are in domain assemblies
[assembly: DomainAssembly(typeof(IDomainMarker))]

public class CreateUserValidator : AbstractValidator<CreateUserCommand>
{
    public CreateUserValidator()
    {
        RuleFor(x => x.Name).NotEmpty();
    }
}
  1. Manual validator registration:
csharp
// As fallback, register manually
builder.Services.AddValidatorsFromAssemblyContaining<CreateUserValidator>();
  1. Debug validator discovery:
csharp
// Add logging to see what validators are found
var validators = builder.Services
    .Where(s => s.ServiceType.IsGenericType &&
               s.ServiceType.GetGenericTypeDefinition() == typeof(IValidator<>))
    .ToList();

foreach (var validator in validators)
{
    logger.LogInformation("Found validator: {ValidatorType} for {CommandType}",
        validator.ImplementationType, validator.ServiceType.GetGenericArguments()[0]);
}

Validation Rules Not Working

Symptoms:

  • Validation rules are ignored
  • Unexpected validation results
  • Custom validation logic not executing

Solutions:

  1. Check rule syntax:
csharp
public class CreateUserValidator : AbstractValidator<CreateUserCommand>
{
    public CreateUserValidator()
    {
        // Correct
        RuleFor(x => x.Email).NotEmpty().EmailAddress();

        // Incorrect - missing property selector
        // RuleFor("Email").NotEmpty(); // Won't work
    }
}
  1. Test validators independently:
csharp
[Test]
public void Validator_InvalidEmail_ReturnsError()
{
    // Arrange
    var validator = new CreateUserValidator();
    var command = new CreateUserCommand("", "invalid-email");

    // Act
    var result = validator.Validate(command);

    // Assert
    result.IsValid.Should().BeFalse();
    result.Errors.Should().Contain(e => e.PropertyName == "Email");
}
  1. Enable validation debugging:
csharp
// In appsettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "FluentValidation": "Debug",
      "Momentum.ServiceDefaults.Validation": "Debug"
    }
  }
}

Messaging and Event Issues

Events Not Being Published

Symptoms:

  • Integration events not appearing in Kafka
  • Event handlers not being triggered
  • No event-related logs

Solutions:

  1. Verify event definition:
csharp
[EventTopic<User>] // Ensure EventTopic attribute is present
public record UserCreated(
    [PartitionKey] Guid TenantId,
    User User
);
  1. Check event handler return:
csharp
public static async Task<(Result<User>, UserCreated?)> Handle(
    CreateUserCommand command,
    IMessageBus messaging,
    CancellationToken cancellationToken)
{
    // ... business logic ...

    var result = insertedUser.ToModel();
    var createdEvent = new UserCreated(result.TenantId, result); // Must return event

    return (result, createdEvent); // Framework publishes the event
}
  1. Check Kafka configuration:
json
{
    "Kafka": {
        "BootstrapServers": "localhost:9092",
        "EnableAutoCommit": false,
        "GroupId": "myapp-consumer-group"
    }
}
  1. Verify Kafka is running:
bash
# Check Kafka container
docker compose ps kafka

# List Kafka topics
docker compose exec kafka kafka-topics --list --bootstrap-server localhost:9092

# Check topic contents
docker compose exec kafka kafka-console-consumer \
  --bootstrap-server localhost:9092 \
  --topic dev.myapp.public.users.v1 \
  --from-beginning

Event Handlers Not Executing

Symptoms:

  • Events are published but handlers don't run
  • No handler logs
  • Events accumulate in topics

Solutions:

  1. Check handler registration:
csharp
public static class UserCreatedHandler
{
    public static async Task Handle(
        UserCreated userCreated,
        ILogger<UserCreatedHandler> logger,
        CancellationToken cancellationToken)
    {
        logger.LogInformation("Processing UserCreated event for {UserId}", userCreated.User.Id);
        // ... handler logic ...
    }
}

// Handlers are automatically discovered from domain assemblies
[assembly: DomainAssembly(typeof(IDomainMarker))]
  1. Check consumer group configuration:
csharp
// In appsettings.json
{
  "Wolverine": {
    "Kafka": {
      "ConsumerGroupId": "myapp-handlers" // Should be unique per service
    }
  }
}
  1. Monitor handler errors:
csharp
public static async Task Handle(
    UserCreated userCreated,
    ILogger<UserCreatedHandler> logger,
    CancellationToken cancellationToken)
{
    try
    {
        // Handler logic
        await DoSomethingAsync(userCreated, cancellationToken);

        logger.LogInformation("Successfully processed UserCreated event");
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "Failed to process UserCreated event");
        throw; // Re-throw to trigger retry
    }
}

Performance Issues

Slow Database Operations

Symptoms:

  • High response times
  • Database timeouts
  • Connection pool exhaustion

Solutions:

  1. Add database indexes:
sql
-- Check for missing indexes
EXPLAIN ANALYZE SELECT * FROM cashiers WHERE tenant_id = $1 AND email = $2;

-- Add appropriate indexes
CREATE INDEX CONCURRENTLY idx_cashiers_tenant_email
ON cashiers (tenant_id, email);
  1. Monitor query performance:
csharp
public class QueryPerformanceMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<QueryPerformanceMiddleware> _logger;

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();

        try
        {
            await _next(context);
        }
        finally
        {
            stopwatch.Stop();

            if (stopwatch.ElapsedMilliseconds > 1000) // Log slow requests
            {
                _logger.LogWarning("Slow request: {Method} {Path} took {ElapsedMs}ms",
                    context.Request.Method,
                    context.Request.Path,
                    stopwatch.ElapsedMilliseconds);
            }
        }
    }
}
  1. Optimize queries:
csharp
// Use projection to reduce data transfer
var summary = await db.Users
    .Where(u => u.TenantId == tenantId)
    .Select(u => new UserSummary // Only select needed fields
    {
        Id = u.Id,
        Name = u.Name,
        Email = u.Email
    })
    .ToListAsync(cancellationToken);

// Use pagination for large result sets
var pagedUsers = await db.Users
    .Where(u => u.TenantId == tenantId)
    .OrderBy(u => u.Name)
    .Skip((page - 1) * pageSize)
    .Take(pageSize)
    .ToListAsync(cancellationToken);

High Memory Usage

Symptoms:

  • OutOfMemoryException
  • Garbage collection pressure
  • Application crashes

Solutions:

  1. Use streaming for large result sets:
csharp
public static async IAsyncEnumerable<User> GetUsersStreamAsync(
    AppDomainDb db,
    Guid tenantId,
    [EnumeratorCancellation] CancellationToken cancellationToken)
{
    await foreach (var user in db.Users
        .Where(u => u.TenantId == tenantId)
        .AsAsyncEnumerable()
        .WithCancellation(cancellationToken))
    {
        yield return user.ToModel();
    }
}
  1. Dispose resources properly:
csharp
public static async Task<Result<ProcessingResult>> Handle(
    LargeProcessingCommand command,
    AppDomainDb db,
    CancellationToken cancellationToken)
{
    using var transaction = await db.BeginTransactionAsync(cancellationToken);

    try
    {
        // Process data in chunks
        const int batchSize = 1000;
        var processed = 0;

        var query = db.LargeDataSet.Where(d => d.TenantId == command.TenantId);

        await foreach (var batch in query
            .AsAsyncEnumerable()
            .Chunk(batchSize)
            .WithCancellation(cancellationToken))
        {
            await ProcessBatch(batch, db, cancellationToken);
            processed += batch.Length;

            // Force garbage collection periodically
            if (processed % 10000 == 0)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
        }

        await transaction.CommitAsync(cancellationToken);

        return new ProcessingResult { ProcessedCount = processed };
    }
    catch
    {
        await transaction.RollbackAsync(cancellationToken);
        throw;
    }
}
  1. Monitor memory usage:
csharp
public class MemoryMonitoringMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<MemoryMonitoringMiddleware> _logger;

    public async Task InvokeAsync(HttpContext context)
    {
        var beforeGC = GC.GetTotalMemory(false);
        var beforeWorking = Environment.WorkingSet;

        try
        {
            await _next(context);
        }
        finally
        {
            var afterGC = GC.GetTotalMemory(false);
            var afterWorking = Environment.WorkingSet;
            var memoryDelta = afterGC - beforeGC;
            var workingDelta = afterWorking - beforeWorking;

            if (memoryDelta > 10_000_000) // 10MB
            {
                _logger.LogWarning("High memory allocation in request {Path}: GC={GCDelta}MB, Working={WorkingDelta}MB",
                    context.Request.Path,
                    memoryDelta / 1024 / 1024,
                    workingDelta / 1024 / 1024);
            }
        }
    }
}

Authentication and Authorization Issues

JWT Token Problems

Symptoms:

  • 401 Unauthorized responses
  • Token validation failures
  • Claims not found

Solutions:

  1. Verify JWT configuration:
csharp
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = builder.Configuration["Auth:Authority"];
        options.Audience = builder.Configuration["Auth:Audience"];
        options.RequireHttpsMetadata = !builder.Environment.IsDevelopment();

        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                var logger = context.HttpContext.RequestServices
                    .GetRequiredService<ILogger<JwtBearerHandler>>();

                logger.LogError(context.Exception, "JWT authentication failed: {Error}",
                    context.Exception.Message);

                return Task.CompletedTask;
            }
        };
    });
  1. Debug token validation:
csharp
public class TokenDebuggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<TokenDebuggingMiddleware> _logger;

    public async Task InvokeAsync(HttpContext context)
    {
        if (context.Request.Headers.ContainsKey("Authorization"))
        {
            var token = context.Request.Headers.Authorization.FirstOrDefault();
            _logger.LogDebug("Processing request with token: {Token}",
                token?.Substring(0, Math.Min(token.Length, 50)) + "...");
        }
        else
        {
            _logger.LogDebug("Processing request without authorization header");
        }

        await _next(context);

        if (!context.User.Identity?.IsAuthenticated == true)
        {
            _logger.LogWarning("Request completed without authentication for path: {Path}",
                context.Request.Path);
        }
    }
}
  1. Test token manually:
bash
# Decode JWT token
echo "YOUR_JWT_TOKEN" | base64 -d

# Test API endpoint
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" https://localhost:5001/api/users

Authorization Policy Issues

Symptoms:

  • 403 Forbidden responses
  • Authorization policies not working
  • Claims-based authorization failures

Solutions:

  1. Check policy configuration:
csharp
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireAdmin", policy =>
        policy.RequireClaim("role", "admin"));

    options.AddPolicy("SameTenant", policy =>
        policy.AddRequirements(new TenantRequirement()));
});

// Register authorization handlers
builder.Services.AddScoped<IAuthorizationHandler, TenantAuthorizationHandler>();
  1. Debug authorization:
csharp
public class AuthorizationLoggingHandler : IAuthorizationHandler
{
    private readonly ILogger<AuthorizationLoggingHandler> _logger;

    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        _logger.LogDebug("Evaluating authorization for user {UserId} with claims: {Claims}",
            context.User.FindFirst("sub")?.Value,
            string.Join(", ", context.User.Claims.Select(c => $"{c.Type}={c.Value}")));

        foreach (var requirement in context.Requirements)
        {
            _logger.LogDebug("Checking requirement: {RequirementType}", requirement.GetType().Name);
        }

        return Task.CompletedTask;
    }
}

Testing Issues

Integration Test Failures

Symptoms:

  • Tests fail in CI but pass locally
  • Database-related test failures
  • Service dependency issues

Solutions:

  1. Use test containers properly:
csharp
public class IntegrationTestBase : IAsyncLifetime
{
    protected readonly PostgreSqlContainer _dbContainer;
    protected readonly KafkaContainer _kafkaContainer;
    protected string ConnectionString => _dbContainer.GetConnectionString();

    public IntegrationTestBase()
    {
        _dbContainer = new PostgreSqlBuilder()
            .WithDatabase("test_db")
            .WithUsername("test_user")
            .WithPassword("test_password")
            .Build();

        _kafkaContainer = new KafkaBuilder()
            .Build();
    }

    public async Task InitializeAsync()
    {
        await _dbContainer.StartAsync();
        await _kafkaContainer.StartAsync();

        // Run migrations
        await RunMigrations();
    }

    public async Task DisposeAsync()
    {
        await _dbContainer.StopAsync();
        await _kafkaContainer.StopAsync();
    }
}
  1. Isolate tests properly:
csharp
[Fact]
public async Task CreateUser_ValidData_ReturnsCreatedUser()
{
    // Arrange - use unique tenant ID for isolation
    var tenantId = Guid.NewGuid();

    using var scope = _factory.Services.CreateScope();
    var db = scope.ServiceProvider.GetRequiredService<AppDomainDb>();

    // Clean up before test
    await db.Users.Where(u => u.TenantId == tenantId).DeleteAsync();

    var command = new CreateUserCommand(tenantId, "John Doe", "john@example.com");

    // Act
    var result = await SendAsync(command);

    // Assert
    result.IsSuccess.Should().BeTrue();

    // Verify in database
    var user = await db.Users.FirstOrDefaultAsync(u => u.UserId == result.Value.Id);
    user.Should().NotBeNull();
}

Docker and Deployment Issues

Container Startup Problems

Symptoms:

  • Containers fail to start
  • Port binding issues
  • Volume mounting problems

Solutions:

  1. Check Docker Compose configuration:
yaml
version: "3.8"
services:
    app:
        build: .
        ports:
            - "5000:8080" # Map host:container ports correctly
        environment:
            - ASPNETCORE_ENVIRONMENT=Development
            - ConnectionStrings__DefaultConnection=Host=db;Database=myapp;Username=postgres;Password=password
        depends_on:
            - db
        networks:
            - myapp-network

    db:
        image: postgres:15
        environment:
            POSTGRES_DB: myapp
            POSTGRES_USER: postgres
            POSTGRES_PASSWORD: password
        ports:
            - "5432:5432"
        volumes:
            - postgres_data:/var/lib/postgresql/data
        networks:
            - myapp-network

volumes:
    postgres_data:

networks:
    myapp-network:
        driver: bridge
  1. Debug container issues:
bash
# Check container logs
docker compose logs app
docker compose logs db

# Exec into container for debugging
docker compose exec app bash

# Check network connectivity
docker compose exec app ping db
docker compose exec app curl http://db:5432

# Check port binding
netstat -tlnp | grep 5000

Health Check Failures

Symptoms:

  • Health check endpoints return unhealthy
  • Load balancer removes instances
  • Kubernetes pods fail readiness checks

Solutions:

  1. Improve health check implementation:
csharp
public class ComprehensiveHealthCheck : IHealthCheck
{
    private readonly AppDomainDb _db;
    private readonly IMessageBus _messageBus;
    private readonly ILogger<ComprehensiveHealthCheck> _logger;

    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default)
    {
        var checks = new List<(string Name, Task<HealthCheckResult> Check)>
        {
            ("Database", CheckDatabaseAsync(cancellationToken)),
            ("Messaging", CheckMessagingAsync(cancellationToken)),
            ("External Services", CheckExternalServicesAsync(cancellationToken))
        };

        var results = await Task.WhenAll(checks.Select(c => c.Check));
        var failed = results.Where(r => r.Status != HealthStatus.Healthy).ToList();

        if (!failed.Any())
        {
            return HealthCheckResult.Healthy("All checks passed");
        }

        var errors = string.Join("; ", failed.Select(f => f.Description));

        if (failed.Any(f => f.Status == HealthStatus.Unhealthy))
        {
            return HealthCheckResult.Unhealthy($"Critical failures: {errors}");
        }

        return HealthCheckResult.Degraded($"Non-critical issues: {errors}");
    }
}
  1. Configure appropriate timeouts:
csharp
builder.Services.AddHealthChecks()
    .AddCheck<ComprehensiveHealthCheck>("comprehensive",
        timeout: TimeSpan.FromSeconds(30),
        tags: ["ready"])
    .AddCheck("quick", () => HealthCheckResult.Healthy(),
        tags: ["live"]);

// Different endpoints with different timeouts
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("live")
});

app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("ready"),
    Timeout = TimeSpan.FromSeconds(30)
});

Monitoring and Observability Issues

Missing Telemetry Data

Symptoms:

  • No traces in monitoring system
  • Missing metrics
  • Logs not appearing

Solutions:

  1. Verify OpenTelemetry configuration:
csharp
// Check exporter configuration
builder.Services.ConfigureOpenTelemetryTracingBuilder(tracing =>
{
    tracing
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddNpgsql()
        .AddJaegerExporter(options =>
        {
            options.Endpoint = new Uri(builder.Configuration["OpenTelemetry:JaegerEndpoint"]!);
        })
        .AddConsoleExporter(); // For debugging
});
  1. Add custom instrumentation:
csharp
public static class TelemetryExtensions
{
    private static readonly ActivitySource ActivitySource = new("MyApp");

    public static async Task<T> TraceAsync<T>(
        this Task<T> task,
        string operationName,
        Action<Activity?>? configure = null)
    {
        using var activity = ActivitySource.StartActivity(operationName);
        configure?.Invoke(activity);

        try
        {
            var result = await task;
            activity?.SetStatus(ActivityStatusCode.Ok);
            return result;
        }
        catch (Exception ex)
        {
            activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
            throw;
        }
    }
}
  1. Debug telemetry pipeline:
csharp
// Add console exporter for debugging
builder.Services.ConfigureOpenTelemetryTracingBuilder(tracing =>
{
    tracing.AddConsoleExporter();
});

// Check for instrumentation
var instrumentation = builder.Services
    .Where(s => s.ServiceType.Name.Contains("Instrumentation"))
    .ToList();

foreach (var instr in instrumentation)
{
    logger.LogInformation("Registered instrumentation: {Type}", instr.ImplementationType);
}

Best Practices for Troubleshooting

Logging Best Practices

  1. Use structured logging:
csharp
// Good
logger.LogInformation("Processing order {OrderId} for customer {CustomerId}",
    orderId, customerId);

// Bad
logger.LogInformation($"Processing order {orderId} for customer {customerId}");
  1. Include correlation IDs:
csharp
public class CorrelationMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        var correlationId = context.Request.Headers["X-Correlation-ID"].FirstOrDefault()
            ?? Guid.NewGuid().ToString();

        context.Items["CorrelationId"] = correlationId;
        context.Response.Headers["X-Correlation-ID"] = correlationId;

        using var scope = logger.BeginScope(new Dictionary<string, object>
        {
            ["CorrelationId"] = correlationId
        });

        await next(context);
    }
}

Error Handling Best Practices

  1. Provide actionable error messages:
csharp
public static Result<User> ValidateUser(CreateUserCommand command)
{
    var errors = new List<ValidationFailure>();

    if (string.IsNullOrEmpty(command.Email))
    {
        errors.Add(new ValidationFailure("Email", "Email is required"));
    }
    else if (!IsValidEmail(command.Email))
    {
        errors.Add(new ValidationFailure("Email", "Please provide a valid email address"));
    }

    return errors.Any() ? errors : Result<User>.Success(/* user */);
}
  1. Log exceptions with context:
csharp
catch (Exception ex)
{
    logger.LogError(ex, "Failed to process command {CommandType} for tenant {TenantId}. Context: {Context}",
        command.GetType().Name,
        command.TenantId,
        new { UserId = context.User?.FindFirst("sub")?.Value, RequestId = context.TraceIdentifier });

    throw;
}

Testing and Debugging

  1. Use debugger effectively:
csharp
[Conditional("DEBUG")]
public static void DebugBreakIf(bool condition, string message = "")
{
    if (condition)
    {
        Debug.WriteLine($"Debug break: {message}");
        Debugger.Break();
    }
}

// Usage
DebugBreakIf(user.TenantId != expectedTenantId, "Tenant mismatch detected");
  1. Create debugging endpoints:
csharp
#if DEBUG
app.MapGet("/debug/health-detailed", async (
    IServiceProvider services) =>
{
    var healthCheckService = services.GetRequiredService<HealthCheckService>();
    var result = await healthCheckService.CheckHealthAsync();

    return Results.Ok(new
    {
        Status = result.Status.ToString(),
        Checks = result.Entries.ToDictionary(
            kvp => kvp.Key,
            kvp => new
            {
                Status = kvp.Value.Status.ToString(),
                Description = kvp.Value.Description,
                Exception = kvp.Value.Exception?.Message,
                Duration = kvp.Value.Duration.TotalMilliseconds
            })
    });
});
#endif

Quick Reference: Common Error Patterns

Error Message Lookup Table

Error Message PatternCategoryQuick Fix
"Assembly not found"StartupAdd [assembly: DomainAssembly(...)]
"Service not registered"DICheck service registration order
"Connection refused"DatabaseVerify database is running and accessible
"Topic not found"MessagingCheck Kafka topic auto-creation settings
"Validation failed"Business LogicCheck FluentValidation rules
"Unauthorized"SecurityVerify JWT configuration and claims
"Timeout"PerformanceCheck database indexes and query performance
"Memory" issuesResourcesProfile application and optimize memory usage

Environment-Specific Troubleshooting

Development Environment

bash
# Quick development reset
docker compose down -v  # Remove all containers and volumes
docker compose up -d postgres kafka  # Start infrastructure
dotnet run --project src/YourApp.Api  # Start application

Production Environment

bash
# Check application health
curl -f http://localhost:5000/health || echo "Health check failed"

# Check resource usage
free -m  # Memory
df -h    # Disk space
top      # CPU and process information

# Check logs
journalctl -u your-app-service --since "1 hour ago"

Docker Environment

bash
# Container diagnostics
docker ps -a                    # List all containers
docker logs container-name      # View container logs
docker exec -it container bash  # Access container shell
docker stats                    # Real-time resource usage

Getting Help and Resources

Self-Service Resources

  1. Momentum Documentation:

  2. Diagnostic Tools:

    • dotnet-trace for performance issues
    • dotnet-dump for memory issues
    • dotnet-counters for real-time metrics
    • dotnet-gcdump for garbage collection analysis
  3. Framework Documentation:

    • ASP.NET Core documentation
    • Entity Framework Core documentation
    • OpenTelemetry documentation
    • Wolverine messaging documentation

Community and Support

  • GitHub Issues - Report bugs and feature requests
  • Stack Overflow - Community Q&A with momentum-dotnet tag
  • Discord/Slack - Real-time community support

Performance Troubleshooting Checklist

  • [ ] Database indexes are properly configured
  • [ ] Connection pooling is optimized
  • [ ] Memory usage is within acceptable limits
  • [ ] CPU usage is not consistently high
  • [ ] Network latency is acceptable
  • [ ] Garbage collection is not excessive
  • [ ] Thread pool is not exhausted
  • [ ] External service calls are optimized

Remember: Always include error messages, configuration (redacted), logs, and steps to reproduce when seeking help.