Skip to content

A .NET 8 Web API application that fetches and stores blockchain data from multiple cryptocurrency networks using the BlockCypher API.

Notifications You must be signed in to change notification settings

devMappouras/BlockchainAPI

Repository files navigation

BlockchainAPI

A .NET 8 Web API application that fetches and stores blockchain data from multiple cryptocurrency networks using the BlockCypher API.

Overview

This application fetches real-time blockchain data from ETH, Dash, BTC, and LTC networks, stores it in a SQL Server database with timestamps, and provides API endpoints to query the historical data.

Data Sources

The application integrates with the BlockCypher API to fetch blockchain data from the following endpoints:

Blockchain Endpoint
Ethereum (Main) https://api.blockcypher.com/v1/eth/main
Dash (Main) https://api.blockcypher.com/v1/dash/main
Bitcoin (Main) https://api.blockcypher.com/v1/btc/main
Bitcoin (Testnet3) https://api.blockcypher.com/v1/btc/test3
Litecoin (Main) https://api.blockcypher.com/v1/ltc/main

Features

Architecture & Design

  • .NET 8 Core application based on Clean Architecture
  • SOLID principles throughout the codebase
  • Dependency Injection for loose coupling
  • Repository pattern for data access
  • Structured logging with Microsoft.Extensions.Logging

API Endpoints

  • RESTful API endpoints exposed via Swagger/OpenAPI
  • Endpoints to fetch and store blockchain data from all supported networks
  • History endpoints showing stored blockchain data ordered by CreatedAt (descending)
  • All HTTP requests documented in Swagger UI

Data Storage

  • SQL Server database for persistent storage
  • Each blockchain record includes a CreatedAt timestamp indicating when the data was fetched
  • Historical data queryable with descending order by creation time

Infrastructure

  • Health Checks endpoint (/health) for monitoring application status
  • CORS Policy configured for cross-origin requests
  • Model mapping using extension methods (ToDto())
  • Comprehensive error handling and logging

API Serialization

  • camelCase property naming for JSON responses (REST API standard)
  • Null value omission - null properties excluded from responses
  • Enum as strings - enums serialized as readable strings instead of numbers

Request Validation

  • FluentValidation for declarative validation rules
  • MediatR Pipeline Behavior for automatic validation before handler execution
  • Structured error responses following RFC 7807 Problem Details format
  • Validation rules for symbols, networks, and query parameters

Quick Start - Docker Setup (2 Ways to Run)

All Docker configuration files are in the Docker/ folder.

Option 1. Quick Way to Start (One Single Command)

Navigate to the Docker folder and run:

cd Docker
docker-compose up -d

This will setup and run both the SQL Server and .NET project. The init db script will run automatically.

Swagger UI: http://localhost:5007/swagger

Health Check: http://localhost:5007/health

Option 2. Start Projects Individually (2 Steps)

1. Run SQL Server in Docker

navigate to the Docker folder by running the command below in the terminal/powershell or use File Explorer: then running the docker-compose command in the terminal/powershell:

cd Docker
docker-compose -f docker-compose.sqlserver.yml up -d

2. Run .NET Project

Option A: Visual Studio - Open solution, select HTTPS configuration, press Run

Option B: Command line:

dotnet run --project BlockchainAPI

Option C: Docker:

cd Docker
docker-compose -f docker-compose.api.yml up -d

Project Structure

BlockchainAPI/
├── BlockchainAPI/                    # API Layer
│   ├── Controllers/                  # API Controllers
│   ├── Middleware/                   # Custom middleware (ValidationExceptionMiddleware)
│   └── Program.cs                    # Application entry point
├── BlockchainAPI.Application/        # Application Layer
│   ├── Behaviors/                    # MediatR pipeline behaviors (ValidationBehavior)
│   ├── CQRS/                         # Commands and Queries with handlers
│   ├── Extensions/                   # Extension methods (BlockchainDataExtensions)
│   ├── Infrastructure/               # Interfaces
│   ├── Models/                       # DTOs
│   └── Validators/                   # FluentValidation validators
├── BlockchainAPI.Domain/             # Domain Layer (Entities)
├── BlockchainAPI.Infrastructure/     # Infrastructure Layer (Services, Repositories, DbContext)
├── BlockchainAPI.Tests/              # Test Project (Unit, Integration, Functional)
│   ├── Unit/                         # Unit tests
│   │   ├── Behaviors/                # ValidationBehavior tests
│   │   ├── Handlers/                 # CQRS handler tests
│   │   ├── Middleware/               # GlobalExceptionMiddleware tests
│   │   ├── Repositories/             # Repository tests
│   │   ├── Services/                 # Service tests
│   │   └── Validators/               # Validator tests
│   ├── Integration/                  # Integration tests
│   └── Functional/                   # End-to-end tests
└── Docker/                           # Docker configuration files

API Endpoints

Method Endpoint Description
GET /api/blockchain/{symbol} Get latest blockchain data for a symbol
GET /api/blockchain/{symbol}/history Get historical data (ordered by CreatedAt desc)
POST /api/blockchain/{symbol}/fetch Fetch and store latest data from BlockCypher
GET /api/blockchain/all Get all supported blockchains' latest data
GET /health Health check endpoint

Supported symbols: eth, dash, btc, btc-test, ltc


Docker Configuration

File Purpose
docker-compose.yml Full stack (SQL Server + API)
docker-compose.sqlserver.yml SQL Server database only
docker-compose.api.yml .NET API only

Docker Images Used

  • Database: mcr.microsoft.com/mssql/server:2022-latest
  • API: mcr.microsoft.com/dotnet/aspnet:8.0

Configuration

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=BlockchainDb;..."
  },
  "BlockCypher": {
    "BaseUrl": "https://api.blockcypher.com/v1",
    "Endpoints": {
      "ETH": "/eth/main",
      "DASH": "/dash/main",
      "BTC": "/btc/main",
      "BTC_TEST": "/btc/test3",
      "LTC": "/ltc/main"
    }
  }
}

Database Schema

BlockchainData Table

Column Type Description
Id int Primary key
Symbol nvarchar(20) Blockchain symbol (ETH, BTC, etc.)
Network nvarchar(20) Network type (main, test3)
Name nvarchar(100) Blockchain name
Height bigint Current block height
Hash nvarchar(100) Latest block hash
Time datetime Block time from API
LatestUrl nvarchar(500) URL to latest block
PreviousHash nvarchar(100) Previous block hash
PreviousUrl nvarchar(500) URL to previous block
PeerCount int Number of peers
UnconfirmedCount int Unconfirmed transactions
HighFeePerKb bigint High fee per KB
MediumFeePerKb bigint Medium fee per KB
LowFeePerKb bigint Low fee per KB
LastForkHeight bigint Last fork height
LastForkHash nvarchar(100) Last fork hash
CreatedAt datetime Timestamp when data was fetched

Testing

The project includes comprehensive test coverage with 119 tests across three testing levels: Unit Tests, Integration Tests and Functional Tests.

Running Tests

# Run all tests
dotnet test

# Run with verbose output
dotnet test --verbosity normal

# Run specific test category
dotnet test --filter "FullyQualifiedName~Unit"
dotnet test --filter "FullyQualifiedName~Integration"
dotnet test --filter "FullyQualifiedName~Functional"

Test Packages Used

Package Purpose
NUnit Test framework
Moq Mocking framework
FluentAssertions Assertion library
Microsoft.AspNetCore.Mvc.Testing Integration testing
Microsoft.EntityFrameworkCore.InMemory In-memory database for tests
Moq.Contrib.HttpClient HTTP client mocking

Unit Tests (91 tests)

Unit tests verify individual components in isolation using mocks for dependencies.

BlockCypherService Tests (Unit/Services/)

Tests for the external API service that fetches blockchain data:

Test Description
GetSupportedBlockchains_ShouldReturnAllSupportedBlockchains Verifies all 5 blockchains are returned
FetchBlockchainDataAsync_WithValidResponse_ShouldReturnMappedData Tests successful API response mapping
FetchBlockchainDataAsync_WithUnsupportedBlockchain_ShouldReturnNull Tests handling of invalid blockchain requests
FetchBlockchainDataAsync_WithCaseInsensitiveSymbol_ShouldWork Verifies case-insensitive symbol handling
FetchBlockchainDataAsync_WithHttpError_ShouldReturnNull Tests graceful HTTP error handling
FetchBlockchainDataAsync_WithInvalidJson_ShouldReturnNull Tests invalid JSON response handling
FetchAllBlockchainsAsync_ShouldFetchAllSupportedBlockchains Tests bulk fetch operation
FetchAllBlockchainsAsync_WithPartialFailure_ShouldReturnOnlySuccessful Tests partial failure scenarios

BlockchainRepository Tests (Unit/Repositories/)

Tests for database operations using EF Core InMemory provider:

Test Description
AddAsync_ShouldAddBlockchainData Verifies data insertion
AddAsync_ShouldAllowMultipleEntriesForSameBlockchain Tests multiple entries per blockchain
GetLatestAsync_WithExistingData_ShouldReturnMostRecent Tests latest record retrieval
GetLatestAsync_WithNoData_ShouldReturnNull Tests empty database handling
GetLatestAsync_ShouldFilterBySymbolAndNetwork Tests filtering logic
GetHistoryAsync_ShouldReturnOrderedByCreatedAtDescending Verifies ordering
GetHistoryAsync_ShouldRespectLimit Tests pagination limit
GetAllLatestAsync_ShouldReturnLatestForEachBlockchain Tests grouped latest retrieval
GetAllLatestAsync_ShouldDistinguishBetweenNetworks Tests network differentiation

CQRS Handler Tests (Unit/Handlers/)

Tests for MediatR command and query handlers:

FetchBlockchainDataCommandHandler:

  • Validates fetch and store workflow
  • Tests null response handling
  • Verifies symbol/network normalization
  • Tests DTO mapping

FetchAllBlockchainsCommandHandler:

  • Tests bulk fetch and store
  • Verifies partial success handling

GetLatestBlockchainQueryHandler:

  • Tests data retrieval and mapping
  • Verifies null handling

GetBlockchainHistoryQueryHandler:

  • Tests history retrieval
  • Verifies limit parameter passing

Validator Tests (Unit/Validators/)

Tests for FluentValidation validators ensuring request data is valid:

FetchBlockchainDataCommandValidator (10 tests):

Test Description
Validate_WithValidSymbolAndNetwork_ShouldPass Tests valid combinations (BTC/main, ETH/main, etc.)
Validate_WithEmptySymbol_ShouldFail Verifies symbol is required
Validate_WithInvalidSymbol_ShouldFail Tests unsupported symbols (DOGE, XRP, etc.)
Validate_WithEmptyNetwork_ShouldFail Verifies network is required
Validate_WithInvalidNetwork_ShouldFail Tests unsupported networks
Validate_WithTest3NetworkForNonBtc_ShouldFail Only BTC supports test3 network

GetLatestBlockchainQueryValidator (10 tests):

  • Same validation rules as FetchBlockchainDataCommand
  • Tests symbol, network, and combination validation

GetBlockchainHistoryQueryValidator (14 tests):

Test Description
Validate_WithValidInput_ShouldPass Tests valid symbol, network, and limit combinations
Validate_WithDefaultLimit_ShouldPass Default limit (100) is valid
Validate_WithZeroLimit_ShouldFail Limit must be at least 1
Validate_WithNegativeLimit_ShouldFail Negative limits are invalid
Validate_WithLimitExceedingMaximum_ShouldFail Limit cannot exceed 1000
Validate_WithMultipleErrors_ShouldReturnAllErrors Multiple validation errors aggregated

GlobalExceptionMiddleware Tests (Unit/Middleware/)

Tests for the global exception handling middleware (17 tests):

Test Description
InvokeAsync_WithArgumentNullException_Returns400BadRequest Maps ArgumentNullException to 400
InvokeAsync_WithUnauthorizedAccessException_Returns401Unauthorized Maps UnauthorizedAccessException to 401
InvokeAsync_WithKeyNotFoundException_Returns404NotFound Maps KeyNotFoundException to 404
InvokeAsync_WithInvalidOperationException_Returns409Conflict Maps InvalidOperationException to 409
InvokeAsync_WithTimeoutException_Returns504GatewayTimeout Maps TimeoutException to 504
InvokeAsync_WithHttpRequestException_Returns502BadGateway Maps HttpRequestException to 502
InvokeAsync_WithGenericException_Returns500InternalServerError Maps unknown exceptions to 500
InvokeAsync_InDevelopment_ShouldIncludeStackTrace Stack traces shown in development
InvokeAsync_InProduction_ShouldNotIncludeStackTrace Stack traces hidden in production
InvokeAsync_ShouldReturnProblemDetailsFormat Returns RFC 7807 format
InvokeAsync_ShouldIncludeRequestPath Includes request path in response
InvokeAsync_ShouldLogError Logs all exceptions

ValidationBehavior Tests (Unit/Behaviors/)

Tests for the MediatR pipeline behavior:

Test Description
Constructor_WithValidators_ShouldNotThrow Behavior accepts validators
Constructor_WithEmptyValidators_ShouldNotThrow Behavior handles no validators
ValidationBehavior_ShouldBeRegisteredAsOpenGeneric Works with different request types

Integration Tests (10 tests)

Integration tests verify the API endpoints work correctly with the full middleware pipeline using WebApplicationFactory and an in-memory database.

Location: Integration/BlockchainControllerIntegrationTests.cs

Test Description
GetSupportedBlockchains_ShouldReturnAllSupported Tests GET /api/blockchain/supported
GetLatest_WithExistingData_ShouldReturnData Tests GET /api/blockchain/{symbol}/{network} with data
GetLatest_WithNoData_ShouldReturn404 Tests 404 response when no data exists
GetLatest_ShouldBeCaseInsensitive Verifies case-insensitive URL handling
GetHistory_ShouldReturnOrderedData Tests history endpoint ordering
GetHistory_ShouldRespectLimit Tests history pagination
GetHistory_WithNoData_ShouldReturnEmptyList Tests empty history response
GetAllLatest_ShouldReturnLatestForEachBlockchain Tests all latest endpoint
FetchAndStore_WithValidData_ShouldReturnStoredData Tests POST fetch endpoint
FetchAndStore_WhenServiceFails_ShouldReturn503 Tests service unavailable response
HealthCheck_ShouldReturnHealthy Tests /health endpoint

Functional Tests (8 tests)

End-to-end tests that simulate complete user workflows through the API.

Location: Functional/BlockchainApiEndToEndTests.cs

Test Description
CompleteUserWorkflow_FetchAndViewBlockchainData Full workflow: check supported → fetch → retrieve → fetch again → view history
BulkFetchWorkflow_FetchAllAndViewAll Tests fetching all blockchains at once
DataConsistencyWorkflow_MultipleFetches Verifies data integrity across multiple fetches
ErrorHandlingWorkflow_ServiceFailure Tests graceful error handling when external API fails
PaginationWorkflow_HistoryWithLimit Tests history pagination with various limits
MultiNetworkWorkflow_SameSymbolDifferentNetworks Tests BTC main vs BTC test3 differentiation

Test Configuration

The integration and functional tests use:

  • In-Memory Database: Each test class gets an isolated database instance using InMemoryDatabaseRoot
  • Mocked External Services: IBlockCypherService is mocked to avoid real API calls
  • Test Environment: Tests run in "Testing" environment to skip migrations
  • HTTPS Handling: Tests use HTTPS base address to handle redirection properly
// Example test setup
_factory = new WebApplicationFactory<Program>()
    .WithWebHostBuilder(builder =>
    {
        builder.UseEnvironment("Testing");
        builder.ConfigureServices(services =>
        {
            // Replace DbContext with in-memory version
            services.AddDbContext<AppDbContext>(options =>
                options.UseInMemoryDatabase(databaseName, databaseRoot));

            // Replace external service with mock
            services.AddSingleton(mockBlockCypherService.Object);
        });
    });

API Serialization

The API uses custom JSON serialization settings for consistent, REST-compliant responses.

Configuration (Program.cs)

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        // Use camelCase for property names (REST API standard)
        options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;

        // Don't include null values in response
        options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;

        // Handle enums as strings instead of numbers
        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
    });

Example Response

Before (default serialization):

{
  "Symbol": "BTC",
  "Network": "main",
  "LastForkHash": null,
  "LastForkHeight": null
}

After (custom serialization):

{
  "symbol": "BTC",
  "network": "main"
}

Request Validation

The API uses FluentValidation with MediatR pipeline behaviors for automatic request validation.

How It Works

Request → Controller → MediatR → ValidationBehavior → Handler
                                        ↓
                                 If invalid:
                           ValidationException thrown
                                        ↓
                        ValidationExceptionMiddleware
                                        ↓
                          400 Bad Request response

Validation Rules

Parameter Rules
Symbol Required, must be: ETH, DASH, BTC, or LTC
Network Required, must be: main or test3
Limit Must be between 1 and 1000
Combination test3 network only valid for BTC

Example Validation Error Response

Request: GET /api/blockchain/INVALID/main

Response (400 Bad Request):

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Symbol": ["Invalid symbol. Supported symbols: ETH, DASH, BTC, LTC"]
  }
}

Key Files

File Purpose
Application/Behaviors/ValidationBehavior.cs MediatR pipeline that runs validators
Application/Validators/*Validator.cs FluentValidation rule definitions
BlockchainAPI/Middleware/ValidationExceptionMiddleware.cs Converts exceptions to HTTP responses

Adding New Validators

// Create validator in Application/Validators/
public class MyCommandValidator : AbstractValidator<MyCommand>
{
    public MyCommandValidator()
    {
        RuleFor(x => x.Property)
            .NotEmpty()
            .WithMessage("Property is required.");
    }
}

// Validators are auto-registered via:
services.AddValidatorsFromAssembly(assembly);

Error Handling

The application implements a comprehensive multi-layered error handling strategy.

Error Handling Architecture

Request → GlobalExceptionMiddleware → ValidationExceptionMiddleware → Controller → Handler
              ↓ (catches all)              ↓ (catches validation)        ↓
         500/4xx responses            400 Bad Request              404/503 responses

1. Global Exception Middleware

Catches all unhandled exceptions and returns consistent RFC 7807 Problem Details responses.

Features:

  • Maps exception types to appropriate HTTP status codes
  • Includes correlation ID for error tracking
  • Hides sensitive details (stack traces) in production
  • Logs all errors with context information

Exception Mapping:

Exception Type HTTP Status Description
ArgumentNullException 400 Bad Request Required parameter was null
ArgumentException 400 Bad Request Invalid parameter
UnauthorizedAccessException 401 Unauthorized Access denied
KeyNotFoundException 404 Not Found Resource not found
InvalidOperationException 409 Conflict Operation conflict
TimeoutException 504 Gateway Timeout Operation timed out
HttpRequestException 502 Bad Gateway External service error
OperationCanceledException 499 Client Closed Request cancelled
All other exceptions 500 Internal Server Error Unexpected error

Example Error Response:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.6.1",
  "title": "An unexpected error occurred.",
  "status": 500,
  "detail": "An error occurred while processing your request. Please try again later.",
  "instance": "/api/blockchain/btc/main",
  "correlationId": "0HN8J4K5L6M7N8O9"
}

2. Validation Exception Middleware

Handles FluentValidation exceptions and returns structured validation error responses.

Example Validation Error:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Symbol": ["Invalid symbol. Supported symbols: ETH, DASH, BTC, LTC"],
    "Limit": ["Limit must be between 1 and 1000"]
  }
}

3. Service-Level Error Handling

BlockCypherService: Gracefully handles external API failures:

try
{
    var response = await _httpClient.GetAsync(url, cancellationToken);
    response.EnsureSuccessStatusCode();
    // Process response...
}
catch (HttpRequestException ex)
{
    _logger.LogError(ex, "HTTP error fetching blockchain data");
    return null;  // Allows caller to handle gracefully
}
catch (JsonException ex)
{
    _logger.LogError(ex, "JSON parsing error");
    return null;
}

4. Controller-Level Error Handling

Controllers return appropriate HTTP status codes for business logic scenarios:

Scenario Status Code Response
Data not found 404 Not Found Helpful message with suggestion
External service failure 503 Service Unavailable Error message
Success 200 OK Requested data

5. Resilience Policies (Polly)

HTTP client resilience for transient failures:

  • Retry: 3 attempts with exponential backoff
  • Circuit Breaker: Opens after 5 failures, 30-second break
  • Timeout: 30-second per-request timeout

Key Files

File Purpose
Middleware/GlobalExceptionMiddleware.cs Catches all unhandled exceptions
Middleware/ValidationExceptionMiddleware.cs Handles FluentValidation errors
Infrastructure/ExternalServices/BlockCypherService.cs Service-level error handling
Controllers/BlockchainController.cs Business logic error responses

Performance Best Practices

The application implements several performance optimizations following .NET best practices.

1. EF Core Query Optimization

AsNoTracking is used for all read-only queries to reduce memory usage and improve query performance:

// Repository read queries don't track entities
var latest = await _context.BlockchainData
    .AsNoTracking()
    .Where(b => b.Symbol == symbol && b.Network == network)
    .OrderByDescending(b => b.CreatedAt)
    .FirstOrDefaultAsync(cancellationToken);

Benefits:

  • No change tracking overhead
  • Reduced memory consumption
  • Faster query execution for read-only scenarios

2. Memory Caching

The repository implements a cache-aside pattern using IMemoryCache for frequently accessed data:

// Cache configuration
private static readonly TimeSpan CacheDuration = TimeSpan.FromSeconds(30);
private const string LatestCacheKeyPrefix = "blockchain_latest_";
private const string AllLatestCacheKey = "blockchain_all_latest";

// Cache-aside pattern
if (_cache.TryGetValue(cacheKey, out BlockchainData? cachedData))
{
    return cachedData; // Cache hit
}

var data = await FetchFromDatabase();
_cache.Set(cacheKey, data, CacheDuration);
return data;

Caching Strategy:

Query Cache Key TTL
GetLatestAsync blockchain_latest_{symbol}_{network} 30 seconds
GetAllLatestAsync blockchain_all_latest 30 seconds
GetHistoryAsync Not cached (variable results) N/A

Cache Invalidation:

  • Cache is automatically invalidated when new data is added via AddAsync
  • Prevents stale data after write operations

3. HTTP Resilience Policies

The application uses Polly for resilient HTTP communication with the BlockCypher API:

// Retry with exponential backoff
var retryPolicy = HttpPolicyExtensions
    .HandleTransientHttpError()
    .WaitAndRetryAsync(3, retryAttempt =>
        TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); // 2s, 4s, 8s

// Circuit breaker - stops calls after 5 failures
var circuitBreakerPolicy = HttpPolicyExtensions
    .HandleTransientHttpError()
    .CircuitBreakerAsync(
        handledEventsAllowedBeforeBreaking: 5,
        durationOfBreak: TimeSpan.FromSeconds(30));

// Request timeout
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(30));

Policy Stack:

  1. Retry Policy: 3 retries with exponential backoff (2s → 4s → 8s)
  2. Circuit Breaker: Opens after 5 consecutive failures, stays open for 30 seconds
  3. Timeout: 30-second timeout per request

4. Asynchronous & Parallel Patterns

The application uses async/await throughout and applies parallel execution where appropriate.

Parallel External API Calls:

// BlockCypherService.cs - Fetch all blockchains in parallel
public async Task<IEnumerable<BlockchainData>> FetchAllBlockchainsAsync(CancellationToken cancellationToken)
{
    var tasks = SupportedBlockchains.Select(b =>
        FetchBlockchainDataAsync(b.Symbol, b.Network, cancellationToken));

    var results = await Task.WhenAll(tasks);  // All 5 API calls run concurrently
    return results.Where(r => r != null).Cast<BlockchainData>();
}

Parallel Validation:

// ValidationBehavior.cs - Run all validators concurrently
var validationResults = await Task.WhenAll(
    _validators.Select(v => v.ValidateAsync(context, cancellationToken)));

Pattern Usage Summary:

Pattern Where Used Why
Task.WhenAll External API calls I/O-bound, independent operations
Task.WhenAll FluentValidation Multiple validators can run concurrently
async/await All layers Non-blocking I/O, thread pool efficiency
Sequential DB writes Repository AddAsync EF Core DbContext is not thread-safe

Design Decisions:

  • Parallel API calls: External HTTP requests to BlockCypher run concurrently since they're independent I/O-bound operations
  • Sequential DB writes: Database operations use sequential execution because EF Core's DbContext is not thread-safe for concurrent operations
  • CancellationToken propagation: All async methods accept and propagate CancellationToken for proper cancellation support

5. Scalability Considerations

Feature Implementation Benefit
Stateless API No session state Horizontal scaling
Connection pooling EF Core default Efficient DB connections
Async/await Throughout codebase Thread efficiency
Dependency injection Scoped services Request isolation
CQRS pattern Separate read/write Optimized query paths

References

BlockCypher API

.NET & Entity Framework

Docker

About

A .NET 8 Web API application that fetches and stores blockchain data from multiple cryptocurrency networks using the BlockCypher API.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages