Introduction
Shared Database Anti-Pattern — Complete Guide is essential for .NET architects building ShopNest Cloud-Native Enterprise Platform — Toolliyo's 120-article microservices master path covering RabbitMQ, Saga, Kubernetes, API Gateway, observability, ASP.NET Core integration, and senior interview preparation. Every article includes minimum 2 detailed production real-world examples (Flipkart, banking, Swiggy, SaaS) in different business domains.
In Indian delivery projects (TCS, Infosys, Wipro), interviewers expect shared database anti-pattern with real Flipkart-scale e-commerce, HDFC-style banking, Swiggy delivery, or SaaS multi-tenant examples — not toy animal demos. This article delivers two mandatory enterprise examples on Identity Service.
After this article you will
- Explain Shared Database Anti-Pattern in plain English and in distributed systems and cloud-native terms
- Implement shared database anti-pattern in ShopNest Cloud-Native Enterprise Platform (Identity Service)
- Compare the wrong approach vs the production-ready enterprise approach
- Answer fresher, mid-level, and senior microservices and distributed systems interview questions confidently
- Connect this lesson to Article 10 and the 120-article Microservices roadmap
Prerequisites
- Software: .NET 8 SDK, VS 2022 or VS Code, SQL Server Express / LocalDB
- Knowledge: C# basics
- Previous: Article 8 — Database Per Service Pattern — Complete Guide
- Time: 22 min reading + 30–45 min hands-on
Concept deep-dive
Level 1 — Analogy
Shared Database Anti-Pattern on ShopNest Cloud-Native extends the distributed platform — each service owns its data and deployment lifecycle.
Level 2 — Technical
Shared Database Anti-Pattern integrates with the LINQ query layer: write queries against IEnumerable or IQueryable, understand deferred execution, project to DTOs for ShopNest Cloud-Native reports. On ShopNest Cloud-Native this powers Identity Service without coupling UI to database internals.
Level 3 — Architecture
[Browser] → [HTTPS/Kestrel] → [Middleware Pipeline]
→ [Routing] → [Controller Action] → [Service Layer]
→ [EF Core / Identity] → [Razor View Engine] → [HTML Response]
Common misconceptions
❌ MYTH: Shared Database Anti-Pattern is only needed for large enterprise apps.
✅ TRUTH: ShopNest Cloud-Native starts simple — add complexity when traffic, team size, or compliance demands it.
❌ MYTH: Web API 2 and ASP.NET Core Web API are the same.
✅ TRUTH: Push filtering, sorting, and aggregation to IQueryable so SQL Server does the work — avoid client-side evaluation.
❌ MYTH: You can call .ToList() first and filter in memory — it works for small data.
✅ TRUTH: Never materialize early on large datasets — filter and project in IQueryable, watch for multiple enumeration.
Project structure
ShopNest Cloud-Native/
├── ShopNest.Cloud/
├── src/
│ ├── Gateway/ ← YARP API Gateway (JWT, rate limit)
│ ├── Services/
│ │ ├── Identity.Api/
│ │ ├── User.Api/
│ │ ├── Product.Api/
│ │ ├── Order.Api/
│ │ ├── Payment.Api/
│ │ ├── Inventory.Api/
│ │ ├── Notification.Api/
│ │ └── Analytics.Api/
│ ├── BuildingBlocks/ ← EventBus, Outbox, Polly policies
│ └── docker-compose.yml
├── k8s/ ← Helm charts per service
└── .github/workflows/ ← CI/CD per service
Step-by-Step Implementation — ShopNest (Identity Service)
Follow the prompt template: create project → core classes → interfaces → pattern implementation → client code → run → enterprise refactor.
Step 1 — The wrong way
// ❌ BAD — fat controller, no ViewModel, sync DB call
public IActionResult Index()
{
return _context.Products.Find(id); // sync, exposes entity, no auth
}
Step 2 — The right way
// ✅ CORRECT — Shared Database Anti-Pattern on ShopNest (Identity Service)
var results = await _context.Products
.Where(p => p.IsPublished && p.CategoryId == categoryId)
.OrderBy(p => p.Name)
.Select(p => new ProductReportDto { Id = p.Id, Name = p.Name, Revenue = p.Orders.Sum(o => o.Total) })
.ToListAsync(ct);
Step 3 — Apply Shared Database Anti-Pattern
// Areas/Admin/Controllers/DashboardController.cs
// Areas/Admin/Views/Dashboard/Index.cshtml
docker compose up --build
# Verify Shared Database Anti-Pattern — check RabbitMQ management UI and kubectl get pods and integration tests pass
Distributed system challenges — Shared Database Anti-Pattern
Production microservices fail in predictable ways. ShopNest engineers plan for these explicitly:
- Network failures — Payment service timeout must not hang Order API thread pool; use Polly timeout + async messaging
- Eventual consistency — Inventory may lag 200ms after order; UI shows "confirming stock" not silent wrong state
- Duplicate messages — RabbitMQ redelivery requires idempotent consumers (Idempotency-Key, unique constraints)
- Retry storms — Exponential backoff + jitter; never retry 503s infinitely without circuit breaker
- Cascade failures — Bulkhead isolates Notification failures from blocking Payment path
Real-World Example 1 — Freshworks-Style SaaS Multi-Tenant Platform
MANDATORY production scenario (Indian SaaS (Freshworks, Zoho)): how Shared Database Anti-Pattern applies in ShopNest Cloud-Native Identity Service.
Business problem
Thousands of tenant organizations share clusters but require schema-level isolation, per-tenant rate limits, and billing meters. Shared-database multi-tenancy caused noisy-neighbor query storms when one tenant ran heavy reports.
Why Shared Database Anti-Pattern matters here
Indian enterprise teams at TCS, Infosys, Wipro, and product companies like Indian SaaS face this exact distributed systems challenge. Shared Database Anti-Pattern is not academic — it directly affects uptime during peak load, deployment frequency, and incident recovery.
Architecture diagram
[Tenant Admin UI] → [YARP Gateway + tenant resolver]
→ [Identity.Service] OAuth2/OIDC
→ [Billing.Service] → Stripe/Razorpay webhooks
→ [Analytics.Service] → read replica per tenant tier
Redis: tenant config cache; API Gateway rate limit per X-Tenant-Id header.
Production implementation
// Tenant resolution middleware — ShopNest.Gateway
app.Map("/api/{**catch-all}", async (HttpContext ctx, IReverseProxy proxy) =>
{
var tenantId = ctx.Request.Headers["X-Tenant-Id"].FirstOrDefault()
?? ctx.User.FindFirst("tenant_id")?.Value;
if (string.IsNullOrEmpty(tenantId))
return Results.Unauthorized();
ctx.Items["TenantId"] = tenantId;
await proxy.SendAsync(ctx, ctx.Request.Path);
});
// EF Core global filter — ShopNest.Product.Api
modelBuilder.Entity<Product>().HasQueryFilter(p => p.TenantId == _tenantProvider.TenantId);
Production metrics and outcome
Noisy-neighbor incidents dropped 94% after database-per-tenant for Enterprise tier; Gateway rate limits stopped trial-abuse DDoS.
Distributed system lessons
- Design for failure — network partitions and partial outages are normal at scale
- Prefer async messaging for cross-service workflows; sync only when latency requires it
- Instrument with OpenTelemetry from day one — you cannot debug what you cannot trace
- Run load tests before Big Billion Day / salary-day / lunch-rush peaks
Real-World Example 2 — Uber-Style Ride Booking Platform
MANDATORY production scenario (Uber / Ola (India)): how Shared Database Anti-Pattern applies in ShopNest Cloud-Native Identity Service.
Business problem
Matching riders to drivers requires low-latency gRPC between Location, Pricing, and Dispatch services. Network partitions must not double-assign drivers — Saga orchestration coordinates RideRequested → DriverAssigned → PaymentCaptured with compensating CancelRide.
Why Shared Database Anti-Pattern matters here
Indian enterprise teams at TCS, Infosys, Wipro, and product companies like Uber / Ola face this exact distributed systems challenge. Shared Database Anti-Pattern is not academic — it directly affects uptime during peak load, deployment frequency, and incident recovery.
Architecture diagram
[Rider App] → [BFF] → [Ride.Api]
→ gRPC → [Dispatch.Service] (driver pool in Redis)
→ [Pricing.Service] surge multiplier
→ [Payment.Service] pre-auth hold
Choreography fallback if orchestrator unavailable; OpenTelemetry traces across all hops.
Production implementation
// gRPC — ShopNest.Dispatch.Grpc/Dispatch.proto
service DriverDispatch {
rpc FindNearestDriver (FindDriverRequest) returns (DriverAssignment);
}
// Polly on HttpClientFactory — ShopNest.Ride.Api/Program.cs
builder.Services.AddHttpClient<IPricingClient, PricingClient>()
.AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(2)))
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
Production metrics and outcome
Driver match p99 180ms in Bangalore pilot; circuit breaker prevented cascade when Pricing service degraded.
Distributed system lessons
- Design for failure — network partitions and partial outages are normal at scale
- Prefer async messaging for cross-service workflows; sync only when latency requires it
- Instrument with OpenTelemetry from day one — you cannot debug what you cannot trace
- Run load tests before Big Billion Day / salary-day / lunch-rush peaks
Security checklist (every ShopNest service)
Even non-auth articles must follow: HTTPS only, no secrets in appsettings committed to git, validate JWT on gateway, least-privilege DB users per service, and structured audit logs for Payment/Identity operations.
ASP.NET Core microservices integration — Shared Database Anti-Pattern
Register services in Program.cs, configure MassTransit/RabbitMQ, expose health endpoints for Kubernetes, and use IHttpClientFactory with Polly for sync calls between ShopNest services.
Microservices integration patterns & ASP.NET Core integration
Modern C# 12 implementations use primary constructors, records, and DI. Register pattern abstractions in Program.cs with appropriate lifetimes (Singleton for stateless, Scoped for request-bound, Transient for lightweight factories).
Microservices: Apply Shared Database Anti-Pattern within bounded contexts — each ShopNest service (Orders, Payments, Inventory) owns its pattern implementation.
Architecture comparison & when NOT to use
Compare Shared Database Anti-Pattern with alternative microservices approaches. Avoid overengineering — if a simple function or DI registration suffices, do not force a pattern. Senior architects value judgment over pattern count.
Common errors & fixes
🔴 Mistake 1: Fat controllers with EF Core queries inline
✅ Fix: Move data access to services/repositories; keep controllers thin.
🔴 Mistake 2: Calling .ToList() too early materializing millions of rows into memory
✅ Fix: Defer execution — build IQueryable pipeline, then ToListAsync() once at the end.
🔴 Mistake 3: Filtering in memory after .ToList() instead of in the database query
✅ Fix: Keep filters in IQueryable, use Select projection, paginate with Skip/Take before materialization.
🔴 Mistake 4: Hard-coding connection strings in controllers
✅ Fix: Use appsettings.json + User Secrets locally; Azure Key Vault in production.
Best practices
- 🟢 Use async/await end-to-end for database and I/O calls
- 🟢 Register DbContext as Scoped; avoid capturing it in singletons
- 🟡 Use IQueryable until the last moment; avoid multiple enumeration; project with Select before ToList
- 🟡 Prefer method syntax for complex chains; use query syntax for joins when readability wins
- 🔴 Log structured data with Serilog — include OrderId, UserId, not passwords
- 🔴 Use HTTPS, secure cookies, and authorization policies in production
Interview questions
Fresher level
Q1: What is Shared Database Anti-Pattern in ASP.NET Core MVC?
A: Shared Database Anti-Pattern is a core MVC capability used in ShopNest Cloud-Native for Identity Service. Explain in one sentence, then describe controller/view/service placement.
Q2: How would you implement Shared Database Anti-Pattern on a TCS-style delivery project?
A: Deferred execution, IQueryable pipelines, Select projection, Skip/Take pagination, and SQL logging in development.
Q3: IEnumerable vs IQueryable — when to use which?
A: IEnumerable for in-memory collections; IQueryable for EF Core database queries that translate to SQL.
Mid / senior level
Q4: Explain LINQ deferred execution and query translation briefly.
A: LINQ → Expression Tree → IQueryProvider → SQL (EF) or Iterator (in-memory) → Results.
Q5: Common production mistake with this topic?
A: Skipping validation, exposing secrets in Git, or untested edge cases (null model, unauthorized user).
Q6: .NET LINQ vs SQL — when to push logic to database?
A: Core is cross-platform, faster, cloud-ready; Framework is maintenance mode on Windows/IIS.
Coding round
Implement Shared Database Anti-Pattern for ShopNest Identity Service: show interface, concrete class, DI registration, and xUnit test with mock.
public class SharedDatabaseAnti-PatternPatternTests
{
[Fact]
public async Task ExecuteAsync_ReturnsSuccess()
{
var mock = new Mock();
mock.Setup(s => s.ExecuteAsync(It.IsAny(), default))
.ReturnsAsync(Result.Success("test-id"));
var result = await mock.Object.ExecuteAsync(new Request("test-id"));
Assert.True(result.IsSuccess);
}
}
Summary & next steps
- Article 9: Shared Database Anti-Pattern — Complete Guide
- Module: Module 1: Foundations and Fundamentals · Level: BEGINNER
- Applied to ShopNest Cloud-Native — Identity Service
Previous: Database Per Service Pattern — Complete Guide
Next: Vertical Slice Architecture — Complete Guide
Practice: Add one small feature using today's pattern — commit with feat(microservices): article-09.
FAQ
Q1: What is Shared Database Anti-Pattern?
Shared Database Anti-Pattern helps ShopNest Cloud-Native implement Identity Service using C# 12 LINQ with EF Core where applicable.
Q2: Do I need Visual Studio?
No — .NET 8 SDK with VS Code + C# Dev Kit works. Visual Studio 2022 Community is recommended for MVC scaffolding.
Q3: Is this asked in Indian IT interviews?
Yes — MVC topics from Modules 1–6 appear in TCS, Infosys, Wipro campus drives; architecture modules in lateral hires.
Q4: Which .NET version?
Examples target .NET 8 LTS and .NET 9 with C# 12+ syntax.
Q5: How does this fit ShopNest Cloud-Native?
Article 9 adds shared database anti-pattern to Identity Service. By Article 100 you have a portfolio-ready ShopNest Cloud-Native enterprise database layer.