<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[DotNet Dev]]></title><description><![CDATA[DotNet Dev]]></description><link>https://shriram95.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 11 Sep 2026 10:09:38 GMT</lastBuildDate><atom:link href="https://shriram95.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Modernising .NET Applications for the Cloud Era: A Practical Guide to Microservices Readiness]]></title><description><![CDATA[The seven pillars every engineering team must address to build cloud-native, independently deployable .NET Core services — covering IIS decoupling, observability, resilience, statelessness, and modern]]></description><link>https://shriram95.hashnode.dev/modernising-net-applications-for-the-cloud-era-a-practical-guide-to-microservices-readiness</link><guid isPermaLink="true">https://shriram95.hashnode.dev/modernising-net-applications-for-the-cloud-era-a-practical-guide-to-microservices-readiness</guid><category><![CDATA[dotnet]]></category><category><![CDATA[dotnetcore]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[Microservices architecture]]></category><category><![CDATA[architecture]]></category><category><![CDATA[Architecture Design]]></category><category><![CDATA[#CSharpProgramming ]]></category><dc:creator><![CDATA[Shriram Thangapandian]]></dc:creator><pubDate>Sun, 15 Mar 2026 12:00:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69a6f8aa56428acc6fef18b3/9cc9114d-e5fb-4188-8ab7-f4248e3b94a1.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<table style="min-width:25px"><colgroup><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><em>The seven pillars every engineering team must address to build cloud-native, independently deployable .NET Core services — covering IIS decoupling, observability, resilience, statelessness, and modern DevOps.</em></p></td></tr></tbody></table>

<p><strong>Why This Matters</strong></p>
<p><strong>Monolithic architectures built on IIS served a generation of enterprise .NET applications well — but they were not designed for the demands of modern cloud environments. Development speed slows as codebases grow. A single deployment affects the entire application. Teams cannot release independently. Scaling one component means scaling everything.</strong></p>
<p><strong>Migrating to a microservices architecture on .NET Core addresses all of these constraints, but it demands a deliberate approach. This guide distills the seven foundational technical pillars required to make that transition successfully.</strong></p>
<p><strong>Pillar 1 — Decouple from IIS</strong></p>
<p><strong>Traditional .NET applications are tightly coupled to Internet Information Services (IIS), which limits them to Windows infrastructure. The first step toward a cloud-native architecture is removing that dependency entirely.</strong></p>
<p><strong>The Solution: Kestrel + Self-Contained Deployments</strong></p>
<p><strong>.NET Core ships with Kestrel, a cross-platform, high-performance web server that runs on Linux, macOS, and Windows without any host dependency. When combined with self-contained deployment, each service can be packaged as a standalone executable or container image.</strong></p>
<p><strong>Key benefits of this approach:</strong></p>
<ul>
<li><p>Kestrel runs identically in Docker, Kubernetes, Azure App Service, or on bare metal</p>
</li>
<li><p>Self-contained executables include the .NET runtime — no runtime installation required on the host</p>
</li>
<li><p>Container-first design aligns naturally with CI/CD pipelines and orchestration platforms</p>
</li>
</ul>
<p>dotnet publish -r linux-x64 --self-contained true -p:PublishSingleFile=true</p>
<p>This single command produces a portable, self-contained binary ready for containerisation.</p>
<p><strong>Pillar 2 — Observability: Logs, Metrics, and Traces</strong></p>
<p><strong>In a distributed system, understanding what is happening inside a service — and across service boundaries — is not optional. Observability is the foundation of operational confidence.</strong></p>
<p><strong>Structured Logging</strong></p>
<p><strong>Replace unstructured log strings with structured events using Serilog or Microsoft.Extensions.Logging with structured sinks. Structured logs are queryable, indexable, and dramatically easier to correlate across services.</strong></p>
<p><strong>Distributed Tracing</strong></p>
<p><strong>Implement OpenTelemetry to propagate trace context across service calls. This makes it possible to reconstruct the full execution path of a request as it traverses multiple services — invaluable for diagnosing latency or failures in production.</strong></p>
<p><strong>Metrics</strong></p>
<p><strong>Use System.Diagnostics.Metrics (introduced in .NET 6) or EventCounters to emit custom application metrics. Expose a /metrics endpoint compatible with Prometheus or Azure Monitor for scraping and alerting.</strong></p>
<p><strong>Health Endpoints</strong></p>
<p><strong>Implement the</strong> <a href="http://ASP.NET"><strong>ASP.NET</strong></a> <strong>Core Health Checks API to expose /health/live and /health/ready endpoints. Kubernetes and load balancers use these to determine whether a service instance should receive traffic.</strong></p>
<table style="min-width:25px"><colgroup><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><em>OpenTelemetry is the industry standard. Instrument once and export to any compatible backend — Jaeger, Zipkin, Azure Monitor, or Grafana Tempo — without changing application code.</em></p></td></tr></tbody></table>

<p><strong>Pillar 3 — Decentralised Data Ownership</strong></p>
<p><strong>One of the most consequential architectural decisions in microservices is data isolation. Each service must own its data exclusively. This principle is non-negotiable.</strong></p>
<p><strong>Database per Service</strong></p>
<p><strong>Every microservice should have its own dedicated database, schema, or at minimum its own set of tables with enforced access boundaries. No other service should query those tables directly.</strong></p>
<ul>
<li><p>Prevents tight coupling through shared data models</p>
</li>
<li><p>Enables each service to evolve its schema independently</p>
</li>
<li><p>Eliminates cross-service joins — inter-service data requirements are fulfilled through well-defined APIs or event-driven communication</p>
</li>
</ul>
<p>For large-scale migrations, start by creating dedicated schemas within an existing database, then graduate to separate database instances as team boundaries solidify.</p>
<p><strong>Pillar 4 — Resilience and Reliability</strong></p>
<p><strong>In a microservices environment, failures are not exceptional events — they are expected. A service will occasionally be slow, unavailable, or unreachable. The critical requirement is that a failure in one service does not cascade and bring down the entire system.</strong></p>
<p><strong>Essential Resilience Patterns</strong></p>
<ul>
<li><p><strong>Detect repeated failures and stop sending requests to a degraded downstream service for a configurable period, giving it time to recover:</strong> Circuit Breakers</p>
</li>
<li><p><strong>Automatically retry transient failures — network glitches, brief timeouts — with increasing delays to avoid overwhelming a recovering service:</strong> Retries with Exponential Backoff</p>
</li>
<li><p><strong>Every outbound call must have an explicit timeout. Without one, a slow dependency will exhaust thread pool resources and bring down the caller:</strong> Timeouts</p>
</li>
<li><p><strong>Limit the number of concurrent requests to any single downstream service, preventing one slow dependency from consuming all available resources:</strong> Bulkhead Isolation</p>
</li>
</ul>
<p><a href="http://builder.Services">builder.Services</a>.AddHttpClient&lt;IOrderService, OrderService&gt;()</p>
<p>    .AddStandardResilienceHandler();  // Polly v8 — retry + circuit breaker</p>
<p>Microsoft.Extensions.Http.Resilience (Polly v8) provides these patterns out of the box for HttpClient in .NET 8+.</p>
<p><strong>Pillar 5 — Statelessness</strong></p>
<p><strong>Stateful services — those that store session data, cache data in memory, or write to the local file system — cannot be scaled horizontally. A request routed to a different instance will not find the expected state.</strong></p>
<p><strong>What to Externalise</strong></p>
<ul>
<li><p><strong>Move to a distributed cache such as Redis using IDistributedCache.</strong> <a href="http://ASP.NET"><strong>ASP.NET</strong></a> <strong>Core supports this natively:</strong> Session State</p>
</li>
<li><p><strong>Use Redis or Azure Cache for Redis instead of IMemoryCache for any data that must be consistent across instances:</strong> Application Cache</p>
</li>
<li><p><strong>Replace local file system writes with Azure Blob Storage, AWS S3, or equivalent object storage:</strong> File Storage</p>
</li>
<li><p><strong>Relational (SQL Server, PostgreSQL) and document stores (Cosmos DB, MongoDB) remain valid for persistent application data:</strong> Database State</p>
</li>
</ul>
<p>Stateless services enable horizontal scaling, efficient load balancing, and graceful recovery from instance failures. Any instance can handle any request.</p>
<p><strong>Pillar 6 — Modern DevOps Automation</strong></p>
<p><strong>The architectural improvements above deliver their full value only when paired with automated deployment pipelines. Manual deployments are a bottleneck and a risk.</strong></p>
<p><strong>CI/CD Pipeline Requirements</strong></p>
<ul>
<li><p>Automated build and test on every pull request — unit, integration, and contract tests</p>
</li>
<li><p>Containerised builds using multi-stage Dockerfiles to produce minimal, secure images</p>
</li>
<li><p>Automated image tagging and publishing to a container registry (Azure Container Registry, Docker Hub, etc.)</p>
</li>
<li><p>Blue/green or rolling deployments to eliminate downtime during releases</p>
</li>
<li><p>Automated rollback triggered by health check failures or elevated error rates</p>
</li>
</ul>
<p>GitHub Actions, Azure DevOps Pipelines, and GitLab CI all support these patterns natively for .NET Core services. The goal is a pipeline where merging to main is sufficient to deploy to production safely.</p>
<p><strong>Summary: The Seven Pillars at a Glance</strong></p>
<table style="min-width:50px"><colgroup><col style="min-width:25px"></col><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><strong>Pillar</strong></p></td><td><p><strong>Outcome</strong></p></td></tr><tr><td><p><strong>1. Decouple from IIS</strong></p></td><td><p>Cross-platform, containerisable, self-contained services</p></td></tr><tr><td><p><strong>2. Observability</strong></p></td><td><p>Full visibility into distributed system behaviour</p></td></tr><tr><td><p><strong>3. Data Ownership</strong></p></td><td><p>Independent schema evolution, no cross-service coupling</p></td></tr><tr><td><p><strong>4. Resilience</strong></p></td><td><p>Fault isolation — one failure does not cascade</p></td></tr><tr><td><p><strong>5. Statelessness</strong></p></td><td><p>Horizontal scaling, efficient load balancing</p></td></tr><tr><td><p><strong>6. DevOps Automation</strong></p></td><td><p>Consistent, repeatable, zero-downtime deployments</p></td></tr><tr><td><p><strong>7. Containerisation</strong></p></td><td><p>Portability across environments, cloud-native deployment</p></td></tr></tbody></table>

<p><strong>Closing Thoughts</strong></p>
<p><strong>Modernising a .NET application for the cloud is not a single event — it is an incremental journey. These seven pillars provide a structured framework for that journey. Teams can address them in phases, validating each improvement before moving to the next.</strong></p>
<p><strong>The result is an architecture that is genuinely cloud-ready: independently deployable, horizontally scalable, resilient by design, and fully observable in production.</strong></p>
<table style="min-width:25px"><colgroup><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><em>The monolith did not fail. It succeeded — and then the world changed. Microservices on .NET Core are not a replacement for what worked; they are the evolution required to keep working at scale.</em></p></td></tr></tbody></table>]]></content:encoded></item></channel></rss>