The Singleton pattern is a creational design pattern that restricts a class to instantiate only one object and provides a global point of access to that instance. It ensures controlled access to a single shared resource.
Core Intent
“Ensure a class has only one instance and provide a global point of access to it.”
This is crucial when exactly one object is needed to coordinate actions across the system.
When to Use Singleton
| Scenario | Use Case |
|---|---|
| Single shared resource | Database connection pool, logger, configuration |
| Global state is required | Application settings, cache, theme manager |
| Expensive to create | Thread pool, hardware interface |
| Controlled instantiation | Prevent multiple instances |
| Testing with mocks | Replace singleton in tests |
Avoid when:
- Used for global variables → leads to tight coupling
- Makes unit testing difficult
- Violates Single Responsibility Principle
Best Practice: Use Dependency Injection (DI) with scoped/singleton services instead of static singletons in modern applications.
Where It Is Used in Real Systems
| Domain | Example |
|---|---|
| Logging | LogManager.GetLogger() |
| Configuration | AppSettings.Instance |
| Caching | MemoryCache.Default |
| Database | DbConnectionFactory.Instance |
| IoC Containers | ServiceProvider (scoped singleton) |
Key Benefits
- Controlled access to sole instance
- Reduces memory footprint
- Global access point
- Lazy initialization support
- Can subclass for variations
Real-World Example: E-Commerce Global Configuration Manager
An e-commerce platform needs one centralized configuration for:
- Tax rates per region
- Payment gateway credentials
- Shipping rules
- Feature flags
This configuration is:
- Loaded once from config file / DB
- Accessed globally by all services
- Thread-safe in multi-user environment
C# Implementation (Thread-Safe, Lazy, Modern)
using System;
using System.Collections.Concurrent;
// ==================== CONFIGURATION MODEL ====================
public class TaxRule
{
public string Region { get; set; }
public decimal Rate { get; set; }
}
public class PaymentGateway
{
public string Name { get; set; }
public string ApiKey { get; set; }
public bool IsSandbox { get; set; }
}
public class ShippingRule
{
public decimal MinOrderValue { get; set; }
public decimal Cost { get; set; }
public bool IsFree { get; set; }
}
// ==================== SINGLETON ====================
public sealed class ECommerceConfiguration
{
// Thread-safe lazy initialization (.NET 4+)
private static readonly Lazy<ECommerceConfiguration> _instance
= new Lazy<ECommerceConfiguration>(() => new ECommerceConfiguration());
// Public accessor
public static ECommerceConfiguration Instance => _instance.Value;
// Private constructor - prevents external instantiation
private ECommerceConfiguration()
{
LoadConfiguration();
Console.WriteLine("[Singleton] Configuration loaded and initialized.");
}
// Configuration data
public ConcurrentDictionary<string, TaxRule> TaxRates { get; } = new();
public PaymentGateway PrimaryGateway { get; private set; }
public List<ShippingRule> ShippingRules { get; } = new();
public bool IsMaintenanceMode { get; private set; }
public string AppVersion { get; private set; } = "1.0.0";
// Simulate loading from config file / DB
private void LoadConfiguration()
{
// Tax Rules
TaxRates["MH"] = new TaxRule { Region = "Maharashtra", Rate = 0.18m };
TaxRates["KA"] = new TaxRule { Region = "Karnataka", Rate = 0.18m };
TaxRates["DL"] = new TaxRule { Region = "Delhi", Rate = 0.18m };
// Payment Gateway
PrimaryGateway = new PaymentGateway
{
Name = "Razorpay",
ApiKey = "rzp_live_abc123",
IsSandbox = false
};
// Shipping
ShippingRules.Add(new ShippingRule { MinOrderValue = 500m, Cost = 0m, IsFree = true });
ShippingRules.Add(new ShippingRule { MinOrderValue = 0m, Cost = 50m, IsFree = false });
IsMaintenanceMode = false;
}
// Optional: Allow controlled updates (e.g., hot reload)
public void Refresh()
{
Console.WriteLine("[Singleton] Refreshing configuration...");
LoadConfiguration();
}
// Prevent serialization attacks
[System.Runtime.Serialization.OnDeserialized]
private void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
{
throw new InvalidOperationException("Deserialization of Singleton is not allowed.");
}
}
// ==================== CLIENT USAGE ====================
public class OrderService
{
private readonly ECommerceConfiguration _config;
public OrderService()
{
_config = ECommerceConfiguration.Instance; // Global access
}
public decimal CalculateTax(string region, decimal subtotal)
{
if (_config.TaxRates.TryGetValue(region, out var rule))
return subtotal * rule.Rate;
return subtotal * 0.18m; // Default
}
public decimal CalculateShipping(decimal orderValue)
{
var freeRule = _config.ShippingRules.Find(r => r.IsFree && orderValue >= r.MinOrderValue);
if (freeRule != null) return 0m;
var standard = _config.ShippingRules.Find(r => !r.IsFree);
return standard?.Cost ?? 50m;
}
public bool IsGatewayAvailable()
{
return !string.IsNullOrEmpty(_config.PrimaryGateway?.ApiKey);
}
}
public class AdminPanel
{
public void ToggleMaintenance()
{
var config = ECommerceConfiguration.Instance;
config.GetType().GetProperty("IsMaintenanceMode")?
.SetValue(config, !config.IsMaintenanceMode);
Console.WriteLine($"[Admin] Maintenance Mode: {config.IsMaintenanceMode}");
}
}
// ==================== DEMO ====================
class Program
{
static void Main()
{
Console.WriteLine("=== SINGLETON PATTERN IN E-COMMERCE ===\n");
// Multiple services use the SAME instance
var orderService1 = new OrderService();
var orderService2 = new OrderService();
var admin = new AdminPanel();
Console.WriteLine($"Same instance? {ReferenceEquals(
ECommerceConfiguration.Instance,
ECommerceConfiguration.Instance)}"); // True
decimal tax = orderService1.CalculateTax("MH", 1000m);
decimal shipping = orderService1.CalculateShipping(600m);
Console.WriteLine($"Tax: ₹{tax}, Shipping: ₹{shipping} (Free over ₹500)");
Console.WriteLine($"Gateway: {ECommerceConfiguration.Instance.PrimaryGateway.Name}");
// Admin can refresh config
admin.ToggleMaintenance();
ECommerceConfiguration.Instance.Refresh();
// Prove singleton across threads
var t1 = new Thread(() => Console.WriteLine($"[Thread1] Version: {ECommerceConfiguration.Instance.AppVersion}"));
var t2 = new Thread(() => Console.WriteLine($"[Thread2] Version: {ECommerceConfiguration.Instance.AppVersion}"));
t1.Start(); t2.Start();
t1.Join(); t2.Join();
}
}Sample Output
=== SINGLETON PATTERN IN E-COMMERCE ===
[Singleton] Configuration loaded and initialized.
Same instance? True
Tax: ₹180, Shipping: ₹0 (Free over ₹500)
Gateway: Razorpay
[Admin] Maintenance Mode: True
[Singleton] Refreshing configuration...
[Singleton] Configuration loaded and initialized.
[Thread1] Version: 1.0.0
[Thread2] Version: 1.0.0UML Class Diagram

Participants in the Singleton Pattern
The classes and objects participating in this pattern include:
- Singleton (ECommerceConfiguration)
- Defines a static method (GetInstance) that returns the single instance.
- Has a private constructor to prevent external instantiation.
- Holds a static reference to the sole instance.
- Client
- Accesses the singleton via ECommerceConfiguration.GetInstance().
Summary Table
| Participant | Role in E-Commerce Example |
|---|---|
| Singleton | ECommerceConfiguration – one global config |
| Client | OrderService, AdminPanel – access via Instance |
Thread-Safe Singleton Variants
| Approach | Code | When to Use |
|---|---|---|
| Lazy<T> (Recommended) | private static readonly Lazy<T> _instance = new(() => new T()); | .NET 4+, clean, thread-safe |
| Double-Checked Lock | if (_instance == null) lock { if (_instance == null) _instance = new(); } | Pre-.NET 4 |
| Static Constructor | static Singleton() { _instance = new(); } | Eager loading |
| Nested Lazy | private class Holder { static readonly T Instance = new(); } | Java-style |
Modern Alternative: Dependency Injection
// In Startup.cs (ASP.NET Core)
services.AddSingleton<ECommerceConfiguration>();
// Injected into services
public OrderService(ECommerceConfiguration config) { ... }Preferred in enterprise apps.
Advantages in E-Commerce
| Benefit | Impact |
|---|---|
| Single config source | No duplication |
| Hot reload | Update without restart |
| Thread-safe | Safe in web farms |
| Global access | Any module can read settings |
Common Pitfalls
| Issue | Solution |
|---|---|
| Testing difficulty | Use interface + DI |
| Memory leaks | Avoid static event subscriptions |
| Serialization | Block with OnDeserialized |
| Global state bugs | Prefer scoped services |
Conclusion
The Singleton pattern is perfect for e-commerce configuration, logging, and caching where one controlled instance is required. Use .NET Lazy<T> for thread-safety.
Pro Tip: In modern systems, prefer DI containers (AddSingleton) over manual singletons for testability and flexibility.
Used in ASP.NET Core, Entity Framework, Serilog, and enterprise middleware.




