Thursday, 11 December 2025

circuit breaker pattern in .net core

 Below is a clear, interview-ready and practical explanation of the Circuit Breaker pattern in .NET Core, along with real-time examples and code using Polly (the standard library for resilience in .NET).


Circuit Breaker Pattern in .NET Core

What it is

Circuit Breaker is a resilience pattern used to prevent your system from repeatedly calling a failing external service (API/database/message broker).
It protects the system from cascading failures.


🔌 States of Circuit Breaker

State Meaning
Closed Everything works normally (calls allowed).
Open Calls blocked immediately because external service is failing.
Half-Open Allows limited trial calls to see if the service recovered.

⭐ Why we use it

  • To avoid hitting a failing service over & over

  • To prevent resource consumption (threads, CPU, DB connections)

  • To improve latency by quickly failing requests

  • To give time for the external system to recover


📦 Circuit Breaker in .NET Core using Polly

Install package:

dotnet add package Polly.Extensions.Http

✔️ Simple Circuit Breaker Example

services.AddHttpClient("StockApiClient")
    .AddTransientHttpErrorPolicy(policy => 
        policy.CircuitBreakerAsync(
            handledEventsAllowedBeforeBreaking: 3,      // After 3 failures
            durationOfBreak: TimeSpan.FromSeconds(30),  // Keep circuit open for 30 sec
            onBreak: (result, ts) =>
            {
                Console.WriteLine("Circuit opened!");
            },
            onReset: () =>
            {
                Console.WriteLine("Circuit closed!");
            },
            onHalfOpen: () =>
            {
                Console.WriteLine("Circuit half-open: trying test call.");
            }
        ));

Meaning:

  • If API fails 3 times → Circuit goes OPEN

  • For the next 30 seconds → all calls fail immediately

  • After 30 sec → Circuit goes HALF-OPEN

  • If next call succeeds → RESET (Closed)

  • If fails → OPEN again


✔️ Circuit Breaker + Retry (Best Practice)

services.AddHttpClient("PaymentService")
    .AddPolicyHandler(
        HttpPolicyExtensions
            .HandleTransientHttpError()
            .RetryAsync(3) // retry 3 times
    )
    .AddPolicyHandler(
        HttpPolicyExtensions
            .HandleTransientHttpError()
            .CircuitBreakerAsync(5, TimeSpan.FromSeconds(20))
    );

Retry handles intermittent failures.
Circuit Breaker handles persistent failures.


✔️ Using Circuit Breaker in Controller

public class StockController : ControllerBase
{
    private readonly IHttpClientFactory _factory;
    public StockController(IHttpClientFactory factory) => _factory = factory;

    [HttpGet("price/{symbol}")]
    public async Task<IActionResult> GetStockPrice(string symbol)
    {
        var client = _factory.CreateClient("StockApiClient");

        var response = await client.GetAsync($"/api/stock/{symbol}");

        if (!response.IsSuccessStatusCode)
            return StatusCode((int)response.StatusCode, "External service failed");

        var data = await response.Content.ReadAsStringAsync();
        return Ok(data);
    }
}

🎯 Real-Time Scenarios Where You Use Circuit Breaker

1. Payment Gateways

If Razorpay/Stripe API is failing, don’t call them continuously.
Circuit stops the calls.

2. Microservices Architecture

If Order Service relies on Inventory Service and Inventory is down →
stop hammering it; return fallback immediately.

3. Slow database or cache

If Redis/Azure SQL is timing out → circuit opens → API returns quick response instead of long delays.

4. Third-party APIs (SMS, Email, Notification)

Avoid getting throttled or blocked by calling continuously.


📘 How You Answer in Interviews (30-second answer)

“Circuit Breaker is a resilience pattern that prevents the system from making calls to a failing external service.
In .NET Core, we implement it using Polly.
After a defined number of failures, the circuit moves to Open state and blocks all calls for a specified time.
This prevents cascading failures and improves performance.
I generally combine Retry + Circuit Breaker in HttpClient when calling microservices or external systems.”



How to Determine User Story Complexity

 Here is a clear, practical and interview-ready explanation for how to determine User Story complexity.

This is one of the most commonly asked Agile/Scrum questions.


How to Determine User Story Complexity

User Story complexity is measured by evaluating three major dimensions:

⭐ 1. Business Complexity

⭐ 2. Technical Complexity

⭐ 3. Testing / Integration Complexity

These dimensions help the team decide how “big” or “complex” the story is, which drives the story points.


🔍 1. Business Complexity

Ask these questions:

✔ Does the story involve multiple business rules?

Example:

  • Tax calculation

  • Trading validations

  • Loan approval logic

✔ How many scenarios or edge cases exist?

More scenarios → more complexity → more points.

✔ Any conditional/branching workflows?

E.g.,

  • "If order > 50K then approval flow; else auto-approve."

➡ More rules = higher complexity.


🔧 2. Technical Complexity

This is the most important factor for developers.

✔ How complex is the architecture impact?

  • Does it touch multiple layers (UI + API + DB)?

  • Requires new database tables?

  • New microservice integration?

✔ Does it involve new technology or unknowns?

  • First time integrating Kafka

  • New cloud service

  • New OAuth-based authentication

👉 Unknowns always increase complexity because of R&D time.

✔ Inter-service communication complexity

  • Async event driven?

  • REST + retry logic?

  • Multiple microservices involved?

✔ Data transformation level

  • Simple JSON

  • Heavy mapping

  • File parsing (Excel, XML, CSV)

More transformation = more complexity.


🔬 3. Testing / Integration / Deployment Complexity

Even if development is simple, testing can raise complexity.

✔ How many test cases?

  • Simple CRUD → Few test cases

  • Workflow system → Many test cases

✔ Any dependency on external systems?

  • Payment gateway

  • Market data provider

  • Third-party APIs

  • External trading FIX engine

External dependencies → delays → more complexity.

✔ Performance concerns

  • High-volume order ingestion

  • Big file upload

  • Realtime latency-sensitive API

Performance testing = increased complexity.

✔ Deployment complexity

  • Requires config changes

  • Feature flags

  • Release dependency between teams


🧩 Putting It All Together (Complexity Formula)

Complexity =

Business rules + Technical effort + Risks/Unknowns + Testing load + External dependencies

High in any category → high story point.


🟦 Real Project Example (Trading App)

Story:

“Add validation: Reject order if price deviates more than 3% from LTP.”

Complexity evaluation:

Factor Details Complexity
Business Simple rule (1 rule) Low
Technical Need LTP from Market Data microservice Medium
External Dependency on real-time market feed High
Testing Multiple test cases for boundary price Medium

👉 Final story point: 5 or 8 (due to dependency + testing complexity)


🟩 Rule of Thumb for Complexity (Quick cheat sheet)

Complexity Level Indicators Story Points
Low Simple CRUD, small UI change, 1–2 acceptance criteria 1–3
Medium Logic, integration with DB, 4–6 AC, validations 5–8
High Multiple systems, complex logic, unknowns, performance, >6 AC 8–13
Very High (Too big) Many modules, multi-sprint, heavy R&D 20+ → Split

🎤 30-second Interview Answer

“To determine user story complexity, we evaluate three things: business complexity, technical complexity, and testing/integration effort. We check how many business rules are involved, how many systems are impacted, whether any new technology or unknowns exist, and how many test cases or dependencies are involved. Based on these factors, we assign relative story points using Fibonacci. Higher dependencies, unknowns, or multi-layer impacts mean higher complexity.”


Here is a ready-to-use, professional User Story Complexity Checklist that you can directly use in interviews, projects, or sprint refinements.


USER STORY COMPLEXITY CHECKLIST

Use this checklist during backlog refinement to determine whether a story is Low / Medium / High complexity.


🔵 1. Business Complexity

Check all applicable items:

✔ Requirements

  • Story has simple requirements (1–2 rules)

  • Story has multiple business rules

  • Story has conditional workflows

  • Story impacts business-critical flow (e.g., orders, payments)

✔ Acceptance Criteria

  • AC count < 3

  • AC count between 3–6

  • AC count > 6 (complex)

✔ Edge Cases

  • Minimal edge cases

  • Many edge cases (boundary validations, alternate flows)


🟠 2. Technical Complexity

Evaluate the technical effort:

✔ Development Effort

  • Only UI / Only API / Only DB

  • UI + API + DB together

  • Touches multiple microservices

  • Requires refactoring or modifying existing architecture

  • Requires new API / new endpoint

  • Requires new DB table, migration, or schema changes

  • Requires file parsing (Excel, CSV, XML)

  • Involves heavy transformations

✔ Unknowns / R&D

  • No unknowns

  • Some unknowns (new flow)

  • Many unknowns (new technology, new system)

✔ Performance

  • No performance concerns

  • Medium performance impact

  • Large volume / low-latency requirement


🟣 3. Integration Complexity

Check how many external systems are involved:

  • No external system

  • 1 internal system

  • Multiple internal systems

  • Third-party API dependency

  • Asynchronous event/message queue involved (Kafka/SQS/Event Grid)

  • Dependency on external vendor (Market data, Payment gateway)

Integration increases complexity significantly.


🟢 4. Testing Complexity

Testing complexity directly impacts story points.

✔ Test Case Volume

  • < 5 test cases

  • 5–15 test cases

  • > 15 test cases

✔ Testing Types

  • Unit tests only

  • API automation

  • UI automation

  • Performance / load testing

  • End-to-end with multiple systems


🟤 5. Deployment / Release Complexity

Check deployment effort:

  • No deployment changes

  • Config change required

  • Feature flag required

  • Coordinated release with other teams

  • Blue-green / canary deployment impact

  • Downtime or migration impact


🚦 Complexity Scoring Guide

Use this simple rule:

Low Complexity (1–3 Points)

  • Only one layer (UI OR API OR DB)

  • Minimal logic

  • No dependency

  • < 3 AC

Medium Complexity (5–8 Points)

  • Multiple layers (UI + API + DB)

  • Some business logic

  • 3–6 AC

  • One integration

High Complexity (8–13 Points)

  • Multiple microservices

  • Heavy business rules

  • Performance concerns

  • External dependencies

  • 6 AC + many test cases

Too Big – Split the Story

  • Impacts multiple epics

  • Multi-sprint work

  • Many unknowns


📝 Final Output Template (You Can Use in Meetings)

User Story Complexity Summary

  • Business Complexity: Low / Medium / High

  • Technical Complexity: Low / Medium / High

  • Integration Complexity: Low / Medium / High

  • Testing Complexity: Low / Medium / High

  • Deployment Complexity: Low / Medium / High

Overall Story Complexity → LOW / MEDIUM / HIGH (1–3 / 5–8 / 8–13 points)


If you'd like, I can also generate:
📌 A one-page PDF of this checklist
📌 A Excel sheet template for your team
📌 A checklist version for Trading, Banking, or E-commerce domains

Using Scoped Services From Singletons in ASP.NET Core

 Did you ever need to inject a scoped service into a singleton service?

I often need to resolve a scoped service, like the EF Core DbContext, in a background service.

Another example is when you need to resolve a scoped service in ASP.NET Core middleware.

If you ever tried this, you were probably greeted with an exception similar to this one:

System.InvalidOperationException: Cannot consume scoped service 'Scoped' from singleton 'Singleton'.

Today, I'll explain how you can solve this problem and safely use scoped services from within singletons in ASP.NET Core.

ASP.NET Core Service Lifetimes

ASP.NET Core has three service lifetimes:

  • Transient
  • Singleton
  • Scoped

Transient services are created each time they're requested from the service container.

Scoped services are created once within the scope's lifetime. For ASP.NET Core applications, a new scope is created for each request. This is how you can resolve scoped services within a given request.

ASP.NET Core applications also have a root IServiceProvider used to resolve singleton services.

So, what can we do if resolving a scoped service from a singleton throws an exception?

The Solution - IServiceScopeFactory

What if you want to resolve a scoped service inside a background service?

You can create a new scope (IServiceScope) with its own IServiceProvider instance. The scoped IServiceProvider can be used to resolve scoped services. When the scope is disposed, all disposable services created within that scope are also disposed.

Here's an example of using the IServiceScopeFactory to create a new IServiceScope. We're using the scope to resolve the ApplicationDbContext, which is a scoped service.

The BackgroundJob is registered as a singleton when calling AddHostedService<BackgroundJob>.

public class BackgroundJob(IServiceScopeFactory serviceScopeFactory)
    : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using IServiceScope scope = serviceScopeFactory.CreateScope();

        var dbContext = scope
            .ServiceProvider
            .GetRequiredService<ApplicationDbContext>();

        // Do some background processing with the EF database context.
        await DoWorkAsync(dbContext);
    }
}

Scoped Services in Middleware

What if you want to use a scoped service in ASP.NET Core middleware?

Middleware is constructed once per application lifetime.

If you try injecting a scoped service, you'll get an exception:

System.InvalidOperationException: Cannot resolve scoped service 'Scoped' from root provider.

There are two ways to get around this.

First, you could use the previous approach with creating a new scope using IServiceScopeFactory. You'll be able to resolve scoped services. But, they won't share the same lifetime as the other scoped service in the same request. This could even be a problem depending on your requirements.

Is there a better way?

Middleware allows you to inject scoped services in the InvokeAsync method. The injected services will use the current request's scope, so they'll have the same lifetime as any other scoped service.

public class ConventionalMiddleware(RequestDelegate next)
{
    public async Task InvokeAsync(
        HttpContext httpContext,
        IMyScopedService scoped)
    {
        scoped.DoSomething();

        await _next(httpContext);
    }
}

IServiceScopeFactory vs. IServiceProvider

You might see examples using the IServiceProvider to create a scope instead of the IServiceScopeFactory.

What's the difference between these two approaches?

The CreateScope method from IServiceProvider resolves an IServiceScopeFactory instance and calls CreateScope() on it:

public static IServiceScope CreateScope(this IServiceProvider provider)
{
    return provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}

So, if you want to use the IServiceProvider directly to create a scope, that's fine.

However, the IServiceScopeFactory is a more direct way to achieve the desired result.

Summary

Understanding the difference between Transient, Scoped, and Singleton lifetimes is crucial for managing dependencies in ASP.NET Core applications.

The IServiceScopeFactory provides a solution when you need to resolve scoped services from singletons. It allows you to create a new scope, which you can use to resolve scoped services.

In middleware, we can inject scoped services into the InvokeAsync method. This also ensures the services use the current request's scope and lifecycle.


Not only the repository pattern in the .Net core, but IServiceScopeFactory also solves similar requirements


In .NET Core, dependency injection (DI) is a core feature for managing and controlling the lifecycle of dependencies (services). However, in some cases, you need more control over how long services live, especially for services that are scoped. This is where the IServiceScopeFactory comes in.

Embark on a journey of continuous learning and exploration with DotNet-FullStack-Dev. Uncover more by visiting our https://dotnet-fullstack-dev.blogspot.com reach out for further information.

Join Medium for free to get updates from this writer.

IServiceScopeFactory is an interface that helps create a scoped lifetime for services manually. Scoped services are created for each request in a web application, but sometimes you need to create these scopes explicitly outside of the typical request pipeline. IServiceScopeFactory allows you to do this, providing flexibility in managing service lifetimes.

When to Use IServiceScopeFactory

You typically use IServiceScopeFactory when:

  • You need to resolve scoped services outside of the standard HTTP request pipeline.
  • You are working with background tasks or hosted services where there is no request context but you still need scoped dependencies.
  • You want to explicitly control the lifetime of services, such as in multi-threaded scenarios or long-running processes.

How IServiceScopeFactory Works

  • Scope: A service scope is an isolated container for scoped services. When the scope is created, the services inside the scope have a limited lifetime, and they are disposed of when the scope ends.
  • Service Resolution: Services inside the scope are resolved through the service provider of that scope. This allows you to handle scoped services independently from the main DI container.

Example: Using IServiceScopeFactory in a Background Task

Consider a scenario where you need to run background tasks that depend on a scoped service, such as accessing the database. In a background task, there is no HTTP request, but you still need to create a scope manually to resolve the scoped services.

Step 1: Define a Scoped Service

public interface IItemService
{
Task ProcessItemsAsync();
}

public class ItemService : IItemService
{
private readonly ApplicationDbContext _dbContext;

public ItemService(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}

public async Task ProcessItemsAsync()
{
// Fetch and process items from the database
var items = await _dbContext.Items.ToListAsync();
// Process items...
}
}

Here, ItemService is a scoped service that depends on ApplicationDbContext, which is also scoped.

Step 2: Use IServiceScopeFactory in a Background Task

Let’s assume that you need to process items in the background periodically.

public class ItemProcessingBackgroundService : BackgroundService
{
private readonly IServiceScopeFactory _serviceScopeFactory;

public ItemProcessingBackgroundService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var itemService = scope.ServiceProvider.GetRequiredService<IItemService>();

// Call the scoped service to process items
await itemService.ProcessItemsAsync();
}

// Wait for some time before running the next iteration
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
}

In this example:

  • IServiceScopeFactory is injected into the background service.
  • The ExecuteAsync method creates a scope for each iteration of the background task using CreateScope().
  • Inside the scope, the IItemService (which is a scoped service) is resolved and used.
  • The scope ensures that the scoped services (like ApplicationDbContext) are created and disposed of correctly with each iteration.

Benefits of Using IServiceScopeFactory

  • Control over Scope Lifetime: You have precise control over when scoped services are created and disposed.
  • Resource Management: Scoped services are disposed automatically when the scope is disposed, ensuring proper cleanup and reducing memory leaks.
  • Non-Request Scenarios: It allows scoped services to be used in non-request-driven processes like background services, hosted services, or multi-threaded tasks.

Common Use Cases

  1. Background Services: When running long-lived background services that need to interact with scoped services, such as database access.
  2. Hosted Services: When implementing IHostedService or BackgroundService, you might need to use scoped services outside of the HTTP request pipeline.
  3. Multi-Threaded Scenarios: When running operations in parallel where each thread requires its own scope for dependencies.
  4. Manually Managing Scopes: In complex scenarios where scope lifetime needs to be managed independently of the request/response lifecycle.



Recent Post

circuit breaker pattern in .net core