Skip to content

akkaraponph/iamatomic

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

14 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ”š Summary Table Of Data Consistency Handling

Pattern Consistency Scale Complexity Suitable For
Pessimistic Locking Strong Low Low Monoliths, legacy systems
Saga Pattern Eventual High High Microservices
CQRS Eventual High High Read-heavy systems, DDD
Mediator Depends Med Medium Coordinated command handling
Event Sourcing Eventual High High Auditable systems
Two-Phase Commit Strong Low High Cross-DB transactional ops
Outbox Pattern Eventual High Medium DB + messaging consistency

πŸ”„ I Am Atomic Transaction Patterns

.NET License: MIT Build Status NuGet

Two powerful transaction patterns for modern applications: AtomicService for ACID transactions and SagaPatternService for distributed transactions with eventual consistency.

🎯 Overview

This library offers two complementary approaches to handle transactions in both monolithic and distributed architectures:

Pattern Use Case Consistency Scope Recovery
AtomicService Single service/database Immediate (ACID) Local operations Transaction rollback
SagaPatternService Multiple services Eventual Distributed operations Compensating actions

✨ Features

πŸ›‘οΈ AtomicService (ACID Transactions)

  • Hierarchical Transactions - Parent-child transaction support
  • Enhanced Retry Logic - Configurable retry with exponential backoff
  • Performance Monitoring - Built-in metrics and correlation tracking
  • Multiple Isolation Levels - ReadCommitted, RepeatableRead, Serializable
  • Deadlock Detection - Smart handling of transient database errors
  • Flexible Configuration - Per-operation timeouts and retry policies

🌐 SagaPatternService (Distributed Transactions)

  • Compensating Actions - Automatic rollback through compensation
  • State Persistence - Durable saga state with resume capability
  • Step Orchestration - Sequential, conditional, and parallel execution
  • Error Recovery - Comprehensive error handling and retry strategies
  • Monitoring & Observability - Full saga lifecycle tracking
  • Integration Ready - Works seamlessly with AtomicService

πŸš€ Quick Start

Basic Setup

// Program.cs
using Persistence.Database;

var builder = WebApplication.CreateBuilder(args);

// Configure AtomicService
builder.Services.Configure<AtomicServiceOptions>(options =>
{
    options.MaxRetryAttempts = 3;
    options.TransactionTimeout = TimeSpan.FromMinutes(5);
    options.DefaultIsolationLevel = IsolationLevel.ReadCommitted;
});

// Configure SagaPatternService
builder.Services.Configure<SagaPatternOptions>(options =>
{
    options.StepTimeout = TimeSpan.FromMinutes(5);
    options.MaxRetryAttempts = 3;
    options.EnableStateLogging = true;
});

// Register services
builder.Services.AddScoped<IAtomicService, AtomicService>();
builder.Services.AddScoped<ISagaStateStore, InMemorySagaStateStore>();
builder.Services.AddScoped<ISagaPatternService, SagaPatternService>();

var app = builder.Build();

πŸ“– Usage Examples

AtomicService - Local ACID Transactions

public class OrderService
{
    private readonly IAtomicService _atomicService;

    // Simple atomic operation
    public async Task CreateOrderAsync(Order order)
    {
        await _atomicService.ExecuteAtomicallyAsync(async () =>
        {
            await _orderRepository.CreateAsync(order);
            await _auditRepository.LogAsync(order.Id, "OrderCreated");
            await _notificationService.SendConfirmationAsync(order.CustomerId);
        });
    }

    // Parent-child hierarchical transaction
    public async Task ProcessComplexOrderAsync(Order order)
    {
        await _atomicService.ExecuteAsParentAsync(async () =>
        {
            await CreateOrderAsync(order); // Child transaction
            await _paymentService.ProcessPaymentAsync(order.Payment); // Child transaction
            await _inventoryService.ReserveItemsAsync(order.Items); // Child transaction
        });
    }

    // With custom options
    public async Task ProcessCriticalOrderAsync(Order order)
    {
        var options = new AtomicOperationOptions
        {
            OperationName = "CriticalOrderProcessing",
            IsolationLevel = IsolationLevel.Serializable,
            Timeout = TimeSpan.FromMinutes(10),
            MaxRetryAttempts = 5
        };

        await _atomicService.ExecuteAtomicallyAsync(async () =>
        {
            // Critical business logic here
        }, options);
    }
}

SagaPatternService - Distributed Transactions

public class DistributedOrderService
{
    private readonly ISagaPatternService _sagaService;

    public async Task<SagaExecutionResult> ProcessDistributedOrderAsync(OrderRequest request)
    {
        var context = new OrderSagaContext(request);
        
        var steps = new List<ISagaStep<OrderSagaContext>>
        {
            // Step 1: Process Payment
            SagaStepBuilder.Create<OrderSagaContext>(
                "ProcessPayment", 1,
                executeAction: async (ctx, ct) =>
                {
                    var payment = await _paymentService.ChargeAsync(ctx.OrderRequest.Payment);
                    ctx.PaymentId = payment.Id;
                    return StepResult.Success;
                },
                compensateAction: async (ctx, ct) =>
                {
                    if (!string.IsNullOrEmpty(ctx.PaymentId))
                    {
                        await _paymentService.RefundAsync(ctx.PaymentId);
                    }
                    return StepResult.Success;
                }
            ),

            // Step 2: Reserve Inventory
            SagaStepBuilder.Create<OrderSagaContext>(
                "ReserveInventory", 2,
                executeAction: async (ctx, ct) =>
                {
                    var reservation = await _inventoryService.ReserveAsync(ctx.OrderRequest.Items);
                    ctx.ReservationId = reservation.Id;
                    return StepResult.Success;
                },
                compensateAction: async (ctx, ct) =>
                {
                    if (!string.IsNullOrEmpty(ctx.ReservationId))
                    {
                        await _inventoryService.ReleaseAsync(ctx.ReservationId);
                    }
                    return StepResult.Success;
                }
            ),

            // Step 3: Schedule Shipping
            SagaStepBuilder.Create<OrderSagaContext>(
                "ScheduleShipping", 3,
                executeAction: async (ctx, ct) =>
                {
                    var shipping = await _shippingService.ScheduleAsync(ctx.OrderRequest);
                    ctx.Data["ShippingId"] = shipping.Id;
                    return StepResult.Success;
                },
                compensateAction: async (ctx, ct) =>
                {
                    if (ctx.Data.TryGetValue("ShippingId", out var shippingId))
                    {
                        await _shippingService.CancelAsync(shippingId.ToString()!);
                    }
                    return StepResult.Success;
                }
            )
        };

        return await _sagaService.ExecuteSagaAsync(context, steps);
    }
}

Hybrid Approach - Best of Both Worlds

public class HybridOrderService
{
    public async Task<StepResult> ProcessOrderDataAsync(OrderContext context, CancellationToken ct)
    {
        // Use AtomicService for local consistency within saga steps
        await _atomicService.ExecuteAtomicallyAsync(async () =>
        {
            // Local ACID operations within the distributed saga step
            await _orderRepository.CreateAsync(context.Order);
            await _auditRepository.LogAsync(context.Order.Id, "OrderCreated");
            await _customerRepository.UpdateOrderCountAsync(context.Order.CustomerId);
        }, new AtomicOperationOptions
        {
            OperationName = "SagaStep.LocalOrderData",
            CorrelationData = new Dictionary<string, object>
            {
                ["SagaId"] = context.SagaId,
                ["OrderId"] = context.Order.Id
            }
        });

        return StepResult.Success;
    }
}

🎯 When to Use Which Pattern

Use AtomicService When:

  • βœ… Single service operations - All operations within one service/database
  • βœ… Need immediate consistency - ACID properties required
  • βœ… Traditional database transactions - Relational database operations
  • βœ… Parent-child service relationships - Hierarchical transaction coordination
  • βœ… Short-lived operations - Transactions that complete quickly

Use Saga Pattern When:

  • βœ… Multiple microservices involved - Cross-service coordination needed
  • βœ… External API integrations - Third-party services in the flow
  • βœ… Long-running business processes - Operations that take significant time
  • βœ… Need eventual consistency - Can tolerate temporary inconsistency
  • βœ… Distributed architecture - Services across different databases/systems

Use Both Together When:

  • πŸš€ Each saga step needs local ACID guarantees - Best of both worlds
  • πŸš€ Distributed coordination with local consistency - Hybrid approach
  • πŸš€ Complex business processes - Spanning multiple services with local data integrity

πŸ—οΈ Architecture Overview

graph TB
    A[Client Request] --> B{Transaction Type?}
    
    B -->|Local Operations| C[AtomicService]
    B -->|Distributed Operations| D[SagaPatternService]
    B -->|Hybrid| E[Both Services]
    
    C --> C1[Transaction Scope]
    C --> C2[Retry Logic]
    C --> C3[Performance Monitoring]
    
    D --> D1[Saga Steps]
    D --> D2[Compensation Actions]
    D --> D3[State Persistence]
    
    E --> E1[Saga Orchestration]
    E1 --> E2[Step 1: AtomicService]
    E1 --> E3[Step 2: AtomicService]
    E1 --> E4[Step N: AtomicService]
    
    C1 --> F[Database]
    D3 --> G[Saga State Store]
    E2 --> F
    E3 --> F
    E4 --> F
Loading

πŸ“Š Performance Characteristics

Pattern Latency Throughput Consistency Complexity Scalability
AtomicService Low High Strong Low Vertical
SagaPattern Medium Medium Eventual Medium Horizontal
Hybrid Medium High Mixed High Both

πŸ“š Documentation

Core Documentation

Examples & Tutorials

Advanced Topics

πŸ› οΈ Configuration Options

AtomicService Configuration

public class AtomicServiceOptions
{
    public int MaxRetryAttempts { get; set; } = 3;
    public TimeSpan BaseRetryDelay { get; set; } = TimeSpan.FromMilliseconds(100);
    public TimeSpan TransactionTimeout { get; set; } = TimeSpan.FromMinutes(5);
    public IsolationLevel DefaultIsolationLevel { get; set; } = IsolationLevel.ReadCommitted;
    public bool EnablePerformanceLogging { get; set; } = true;
    public bool EnableDistributedTransactions { get; set; } = false;
}

SagaPatternService Configuration

public class SagaPatternOptions
{
    public TimeSpan StepTimeout { get; set; } = TimeSpan.FromMinutes(5);
    public TimeSpan CompensationTimeout { get; set; } = TimeSpan.FromMinutes(10);
    public int MaxRetryAttempts { get; set; } = 3;
    public TimeSpan RetryDelay { get; set; } = TimeSpan.FromSeconds(1);
    public bool EnableStateLogging { get; set; } = true;
    public bool EnableCompensationLogging { get; set; } = true;
}

πŸ” Monitoring & Observability

Both services provide comprehensive monitoring capabilities:

Built-in Metrics

  • Transaction success/failure rates
  • Operation duration and performance
  • Retry attempt tracking
  • Compensation execution monitoring
  • Resource usage statistics

Logging Integration

  • Structured logging with correlation IDs
  • Performance logging for optimization
  • Error tracking with context
  • Saga state change events

Health Checks

// Built-in health checks
services.AddHealthChecks()
    .AddCheck<AtomicServiceHealthCheck>("atomic-service")
    .AddCheck<SagaPatternHealthCheck>("saga-pattern");

πŸ§ͺ Testing

Unit Testing

[Test]
public async Task AtomicService_Should_Rollback_On_Exception()
{
    // Arrange
    var mockRepository = new Mock<IRepository>();
    mockRepository.Setup(x => x.SaveAsync(It.IsAny<Entity>()))
              .ThrowsAsync(new InvalidOperationException());

    // Act & Assert
    await Assert.ThrowsAsync<InvalidOperationException>(() =>
        _atomicService.ExecuteAtomicallyAsync(async () =>
        {
            await mockRepository.Object.SaveAsync(new Entity());
        }));
    
    // Verify rollback occurred
    mockRepository.Verify(x => x.SaveAsync(It.IsAny<Entity>()), Times.Once);
}

Integration Testing

[Test]
public async Task SagaPattern_Should_Compensate_On_Step_Failure()
{
    // Arrange
    var context = new TestSagaContext();
    var steps = CreateTestStepsWithFailure();

    // Act
    var result = await _sagaService.ExecuteSagaAsync(context, steps);

    // Assert
    Assert.That(result.FinalStatus, Is.EqualTo(SagaStatus.Compensated));
    Assert.That(context.CompensatedSteps.Count, Is.GreaterThan(0));
}

About

Atomic Hanlding C# | Consistency within a single transaction scope

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published