<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Татьяна Кузнецова</title>
    <description>The latest articles on Forem by Татьяна Кузнецова (@belochka1-04).</description>
    <link>https://forem.com/belochka1-04</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3684256%2Fd8784611-b32a-407b-a7b7-1c39b8300259.png</url>
      <title>Forem: Татьяна Кузнецова</title>
      <link>https://forem.com/belochka1-04</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/belochka1-04"/>
    <language>en</language>
    <item>
      <title>Authorization methods in .NET microservices</title>
      <dc:creator>Татьяна Кузнецова</dc:creator>
      <pubDate>Fri, 20 Feb 2026 10:49:41 +0000</pubDate>
      <link>https://forem.com/belochka1-04/authorization-methods-in-net-microservices-1dl0</link>
      <guid>https://forem.com/belochka1-04/authorization-methods-in-net-microservices-1dl0</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5wrf5axev01zffydq9k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5wrf5axev01zffydq9k.png" alt="Authorization methods in .NET microservices" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today I want to dive into &lt;strong&gt;one of the most critical stages of microservices development: authorization&lt;/strong&gt;. This stage can bring &lt;strong&gt;significant complexity&lt;/strong&gt;. &lt;strong&gt;Each service is now a separate HTTP server with its own endpoints&lt;/strong&gt;, and the client (bot, frontend, mobile app) must have &lt;strong&gt;simultaneous access to all of them&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Theory: Authentication vs Authorization in Microservices
&lt;/h2&gt;

&lt;p&gt;In microservices architecture, it's crucial to distinguish between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt; — &lt;em&gt;who is this?&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization&lt;/strong&gt; — &lt;em&gt;what is this subject allowed to do?&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Authentication is typically handled by a &lt;strong&gt;dedicated Identity Provider (IdP)&lt;/strong&gt; or &lt;strong&gt;Auth service&lt;/strong&gt;, while microservices accept already-validated tokens and decide what the client can access.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv02f3zfwiqdtyn8k6wb0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv02f3zfwiqdtyn8k6wb0.png" alt="High-level microservices security architecture" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Main Authorization Approaches in Microservices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Session-based authorization (cookies + server-side session store)&lt;/li&gt;
&lt;li&gt;Token-based:
-&amp;gt; JWT (JSON Web Token)
-&amp;gt; Opaque tokens&lt;/li&gt;
&lt;li&gt;OAuth 2.0 / OpenID Connect (OIDC) on top of tokens (usually JWT)&lt;/li&gt;
&lt;li&gt;API Keys&lt;/li&gt;
&lt;li&gt;mTLS (mutual TLS) — mutual TLS authentication between services&lt;/li&gt;
&lt;li&gt;Combinations (e.g., OIDC+JWT for users + mTLS between services)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🔐 JWT&lt;/strong&gt; — compact &lt;strong&gt;signed token&lt;/strong&gt; with claims (&lt;code&gt;sub&lt;/code&gt;, &lt;code&gt;roles&lt;/code&gt;, &lt;code&gt;scopes&lt;/code&gt;, etc.) that can be validated &lt;strong&gt;locally&lt;/strong&gt; without accessing shared state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🌐 OAuth2/OIDC&lt;/strong&gt; — protocols defining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;How clients obtain tokens&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;How SSO is implemented&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Token format (JWT or opaque) is a separate layer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📈 Approach Features &amp;amp; 2026 Trends
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Key trend&lt;/strong&gt;: Moving authentication (and &lt;strong&gt;part of authorization&lt;/strong&gt;) to a &lt;strong&gt;centralized IdP/Auth-hub&lt;/strong&gt;, while microservices become &lt;strong&gt;"resource servers"&lt;/strong&gt; that trust validated tokens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Centralized IdP/Auth-hub + Resource Servers Pattern
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Centralized IdP&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores user accounts, login methods, MFA, social login, etc.&lt;/li&gt;
&lt;li&gt;Implements OAuth2/OIDC protocols and issues tokens (access, refresh, ID token)&lt;/li&gt;
&lt;li&gt;Serves as the &lt;strong&gt;single source of truth&lt;/strong&gt; for authentication and basic roles/groups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Resource Servers (microservices)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Host protected resources&lt;/li&gt;
&lt;li&gt;Accept &lt;strong&gt;only requests with valid tokens&lt;/strong&gt; (OAuth2 terminology)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Simultaneous zero-trust hardening&lt;/strong&gt;: Even inside the cluster, services explicitly authenticate (&lt;strong&gt;mTLS&lt;/strong&gt;, &lt;strong&gt;certificates&lt;/strong&gt;, &lt;strong&gt;SPIFFE&lt;/strong&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Overview of Approaches
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Key Characteristics&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sessions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simpler for classic web apps/monoliths. Requires shared session store or sticky sessions in microservices (scaling complexity).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JWT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Stateless&lt;/strong&gt; approach, perfect for microservices + API gateway. &lt;strong&gt;De facto standard&lt;/strong&gt; for OAuth2/OIDC access tokens in 2026.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OAuth2/OIDC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Industry standard for external clients, mobile, SPA, B2B. Easy SSO, MFA, social login via IdP.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple keys for machine-to-machine access/integrations. Often combined with IP limits, rate limiting, gateway policies in 2026.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;mTLS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Standard service-to-service auth in service mesh (&lt;strong&gt;Istio&lt;/strong&gt;, &lt;strong&gt;Linkerd&lt;/strong&gt;, &lt;strong&gt;Consul&lt;/strong&gt;). Guarantees "this pod is exactly that service" but carries no user rights.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Modern production standard&lt;/strong&gt;: &lt;code&gt;API Gateway/Ingress + OAuth2/OIDC + JWT + mTLS inside mesh&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1️⃣ Sessions (Cookies + Server-Side Sessions)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  How It Works
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;- User logs in on server (password, MFA)&lt;/li&gt;
&lt;li&gt;- Server generates unique session ID and stores full session in store (Redis, SQL, Memcached)&lt;/li&gt;
&lt;li&gt;- Session ID written to cookie (typically HttpOnly, Secure, SameSite=Strict/Lax)&lt;/li&gt;
&lt;li&gt;- Subsequent requests → server reads session ID from cookie → fetches session data from store&lt;/li&gt;
&lt;li&gt;- If session found and not expired → user authenticated&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Microservices variants&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sticky sessions&lt;/strong&gt;: Load balancer binds user to one pod/service (by cookie)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared session store&lt;/strong&gt;: All microservices read from central store (Redis cluster)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ✅ Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Implementation simplicity&lt;/strong&gt;: Built into ASP.NET Core, Spring, Rails etc. No protocols to learn&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized control&lt;/strong&gt;: Easy to revoke/end session in store (e.g., suspected compromise)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Cookie contains no sensitive data (just ID), forgery-resistant (proper HttpOnly, Secure)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perfect for SSR/traditional web&lt;/strong&gt;: Browser auto-sends cookies&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ❌ Disadvantages &amp;amp; Microservices Issues
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Scaling: Requires resilient session store → central point of failure&lt;/li&gt;
&lt;li&gt;Redis outage = mass logout of ALL users&lt;/li&gt;
&lt;li&gt;Sticky sessions limit flexibility: Can't freely scale/restart pods, hard multi-region migration&lt;/li&gt;
&lt;li&gt;Performance: Every request = store read (network roundtrip), accumulates across services&lt;/li&gt;
&lt;li&gt;Poor API/mobile/SPA fit: Cookie issues (CORS, cross-domain, native apps)&lt;/li&gt;
&lt;li&gt;CSRF/XSS: Requires extra measures (CSRF tokens, SameSite)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  When to Use
&lt;/h4&gt;

&lt;p&gt;✅ Classic SSR web apps without complex API architecture&lt;br&gt;
✅ Internal admin panels where simplicity &amp;gt; scale&lt;br&gt;
✅ Monolith-to-microservices transition period&lt;br&gt;
❌ Pure REST APIs, SPAs, mobile clients&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2026 status&lt;/strong&gt;: Rarely used in pure microservices, survives in legacy/BFF for web UI.&lt;/p&gt;
&lt;h3&gt;
  
  
  2️⃣ JWT (JSON Web Token)
&lt;/h3&gt;
&lt;h4&gt;
  
  
  How It Works
&lt;/h4&gt;

&lt;p&gt;JWT consists of three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Header&lt;/strong&gt; – signing algorithm
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payload&lt;/strong&gt; – claims (&lt;code&gt;sub&lt;/code&gt;, &lt;code&gt;roles&lt;/code&gt;, &lt;code&gt;scopes&lt;/code&gt;, etc.)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature&lt;/strong&gt; – signed with a private key
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;After login, the Auth service generates a JWT with the required claims and signs it with a &lt;strong&gt;private key&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;The client attaches the JWT to every request in the header:
&lt;code&gt;Authorization: Bearer &amp;lt;jwt_token&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Each microservice &lt;strong&gt;validates locally&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Signature (using IdP public key from JWKS endpoint)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exp&lt;/code&gt; (expiration), &lt;code&gt;iss&lt;/code&gt; (issuer), &lt;code&gt;aud&lt;/code&gt; (audience), &lt;code&gt;nbf&lt;/code&gt; (not before)
&lt;/li&gt;
&lt;li&gt;Claims (roles, scopes, etc.)
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If everything is valid, the service uses claims for authorization.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Signing options&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Symmetric&lt;/strong&gt; (shared secret) — simpler, but worse for distributed systems
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asymmetric&lt;/strong&gt; (RS256/ECDSA) — standard; public key exposed via JWKS&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  ✅ Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stateless&lt;/strong&gt;: No central session store, each service verifies tokens independently — perfect for microservices
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable&lt;/strong&gt;: To add a new service, you just give it the IdP public key
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible claims&lt;/strong&gt;: You can embed roles, tenant, permissions, feature flags — the service sees everything it needs immediately
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard&lt;/strong&gt;: Native support in all major frameworks (ASP.NET Core JwtBearer, Spring Security, etc.)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform&lt;/strong&gt;: Works equally well for browsers, mobile apps, CLI tools, partners&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  ❌ Disadvantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Revocation is hard&lt;/strong&gt;: Until the token expires (usually 15–60 minutes), it remains valid

&lt;ul&gt;
&lt;li&gt;Workarounds: short TTL + refresh tokens, blacklists, introspection endpoints (but this hurts statelessness)
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token size&lt;/strong&gt;: Can be large with many claims, increasing HTTP header size
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claims leakage&lt;/strong&gt;: If a token is captured, the attacker can read roles/rights (though cannot forge signature)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crypto pitfalls&lt;/strong&gt;: Incorrect validation (not checking &lt;code&gt;iss&lt;/code&gt;/&lt;code&gt;aud&lt;/code&gt;/&lt;code&gt;exp&lt;/code&gt;) is a common vulnerability&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  When to Use
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Primary format for access tokens&lt;/strong&gt; in microservice architectures
&lt;/li&gt;
&lt;li&gt;Always &lt;strong&gt;in combination with OAuth2/OIDC&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Standalone — for simple internal APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2026 status&lt;/strong&gt;: De facto &lt;strong&gt;standard access token format&lt;/strong&gt; in OAuth2/OIDC, typically with &lt;strong&gt;short-lived access tokens + refresh tokens&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  3️⃣ OAuth2 / OIDC
&lt;/h3&gt;
&lt;h4&gt;
  
  
  How It Works
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OAuth2&lt;/strong&gt; is an &lt;em&gt;authorization&lt;/em&gt; protocol (how a client obtains a token with specific access rights).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OIDC (OpenID Connect)&lt;/strong&gt; is an identity layer on top of OAuth2 (who is the user).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Roles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource Owner&lt;/strong&gt; — the user
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client&lt;/strong&gt; — SPA, mobile app, BFF
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization Server&lt;/strong&gt; — IdP (Auth server)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Server&lt;/strong&gt; — microservice (API)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Main Flows Used with Microservices
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authorization Code Flow + PKCE&lt;/strong&gt; (for SPA/mobile):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client redirects the user to the IdP
&lt;/li&gt;
&lt;li&gt;After login, IdP returns an authorization &lt;code&gt;code&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Client exchanges the &lt;code&gt;code&lt;/code&gt; for &lt;code&gt;access&lt;/code&gt; and &lt;code&gt;refresh&lt;/code&gt; tokens
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Client Credentials Flow&lt;/strong&gt; (service-to-service):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service uses &lt;code&gt;client_id&lt;/code&gt;/&lt;code&gt;client_secret&lt;/code&gt; to obtain a token &lt;strong&gt;on its own behalf&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implicit Flow&lt;/strong&gt; (legacy, for old SPA apps — considered obsolete)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Device Code Flow&lt;/strong&gt; (for TV/CLI and limited-input devices)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Token Types
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access Token&lt;/strong&gt; — carries access rights (what the client can do)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refresh Token&lt;/strong&gt; — used to obtain new access tokens
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ID Token&lt;/strong&gt; (OIDC) — contains user identity information (who the user is)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  ✅ Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standardized&lt;/strong&gt;: Works with any modern IdP (Keycloak, Auth0, Azure AD, Google, etc.)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSO &amp;amp; federation&lt;/strong&gt;: One login for all services, easy integration with external identity providers
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supports many client types&lt;/strong&gt;: SPA, native apps, backend services, partners
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scopes &amp;amp; delegated access&lt;/strong&gt;: Clients receive only the permissions they actually need (&lt;code&gt;read:orders&lt;/code&gt;, &lt;code&gt;write:payments&lt;/code&gt;, etc.)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security features&lt;/strong&gt;: PKCE against code injection, short-lived tokens, audit trails&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  ❌ Disadvantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: Many moving parts (redirect_uri, scopes, client_id, PKCE), easy to misconfigure
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IdP dependency&lt;/strong&gt;: IdP must be highly available and secure
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend complexity&lt;/strong&gt;: SPA/mobile must correctly handle redirects, token storage, and silent refresh&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  When to Use
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Any &lt;strong&gt;public REST APIs&lt;/strong&gt; with external clients
&lt;/li&gt;
&lt;li&gt;Microservice systems that require &lt;strong&gt;SSO&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B2B integrations&lt;/strong&gt; and partner access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2026 status&lt;/strong&gt;: The &lt;strong&gt;mandatory standard&lt;/strong&gt; for enterprise and public-facing APIs.&lt;/p&gt;
&lt;h3&gt;
  
  
  4️⃣ API Keys
&lt;/h3&gt;
&lt;h4&gt;
  
  
  How It Works
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;A unique key is generated (typically a 32–64 character string, sometimes with a prefix).
&lt;/li&gt;
&lt;li&gt;The client (service, script, partner) sends the key in a header, for example:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;X-API-Key: abc123...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;or &lt;code&gt;Authorization: ApiKey abc123...&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The gateway/microservice validates the key against a database or cache (e.g., Redis).
&lt;/li&gt;
&lt;li&gt;The key is usually associated with:

&lt;ul&gt;
&lt;li&gt;IP restrictions
&lt;/li&gt;
&lt;li&gt;Rate limits
&lt;/li&gt;
&lt;li&gt;Scopes
&lt;/li&gt;
&lt;li&gt;Expiration time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  ✅ Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maximum simplicity&lt;/strong&gt;: No complex protocols, redirects, or crypto handshakes
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast to implement&lt;/strong&gt;: Great for internal tools, CI/CD pipelines, partner systems
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible policies&lt;/strong&gt;: Rate limiting, IP whitelists, daily/monthly quotas
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy revocation&lt;/strong&gt;: Just remove or disable the key in the database&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  ❌ Disadvantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No user identity&lt;/strong&gt;: The key is tied to an application/service, not to an individual user
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coarse-grained access&lt;/strong&gt;: Often “all or nothing” or very rough scopes
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key management overhead&lt;/strong&gt;: Secure issuing, rotation, and audit are required
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No inherent encryption&lt;/strong&gt;: Keys can appear in logs/traffic — TLS is mandatory&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  When to Use
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Internal services and scripts
&lt;/li&gt;
&lt;li&gt;Partner integrations without complex per-user permissions
&lt;/li&gt;
&lt;li&gt;Prototypes and proof-of-concept APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2026 status&lt;/strong&gt;: Commonly used &lt;strong&gt;in combination with API gateways&lt;/strong&gt; (rate limiting + IP checks), but &lt;strong&gt;not the primary method&lt;/strong&gt; for user-facing authorization.&lt;/p&gt;
&lt;h3&gt;
  
  
  5️⃣ mTLS (Mutual TLS)
&lt;/h3&gt;
&lt;h4&gt;
  
  
  How It Works
&lt;/h4&gt;

&lt;p&gt;With regular TLS, &lt;strong&gt;only the server&lt;/strong&gt; presents a certificate to the client.&lt;br&gt;&lt;br&gt;
With &lt;strong&gt;mTLS&lt;/strong&gt;, the verification is &lt;strong&gt;mutual&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;client service&lt;/strong&gt; presents a &lt;strong&gt;client certificate&lt;/strong&gt; (with SAN/pod name).
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;server service&lt;/strong&gt; verifies the client certificate (trusted CA, not expired, correct subject).
&lt;/li&gt;
&lt;li&gt;The server also presents its own certificate.
&lt;/li&gt;
&lt;li&gt;The connection is encrypted, and &lt;strong&gt;both sides know exactly who is on the other end&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  mTLS in a Service Mesh (e.g., Istio)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;sidecar proxy (Envoy)&lt;/strong&gt; automatically handles certificates (via Citadel/SPIRE).
&lt;/li&gt;
&lt;li&gt;Policies in &lt;code&gt;AuthorizationPolicy&lt;/code&gt; / &lt;code&gt;PeerAuthentication&lt;/code&gt; define rules like:
&amp;gt; "OrderService is allowed to call PaymentService, but not the other way around."&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  ✅ Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero trust&lt;/strong&gt;: You do not trust the network; every piece of traffic is authenticated
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic encryption inside the cluster&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability&lt;/strong&gt;: Easy to audit which service talked to which
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy enforcement&lt;/strong&gt;: Mesh-level RBAC based on certificates/identities&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  ❌ Disadvantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PKI complexity&lt;/strong&gt;: Certificate rotation, CA management, revocation
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Config sensitivity&lt;/strong&gt;: One bad policy can break all traffic
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overhead&lt;/strong&gt;: TLS handshakes on connections (though modern hardware handles this well)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not about users&lt;/strong&gt;: mTLS identifies &lt;strong&gt;services&lt;/strong&gt;, not users — user permissions still need JWT/OAuth2/etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  When to Use
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service-to-service&lt;/strong&gt; communication inside a cluster
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-trust&lt;/strong&gt;, enterprise environments, regulated industries
&lt;/li&gt;
&lt;li&gt;Always together with a &lt;strong&gt;service mesh&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2026 status&lt;/strong&gt;: The &lt;strong&gt;standard&lt;/strong&gt; for production microservices running in a mesh, often as part of the &lt;strong&gt;“golden combo”&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
&lt;code&gt;API Gateway + OAuth2/JWT + mTLS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvib9tnkoq3ztgbtdeju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvib9tnkoq3ztgbtdeju.png" alt="High-level comparison of common authentication and authorization methods in microservices" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  🔄 Authorization Flows: Client → Gateway → Microservices
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mb7qshu8a1pwx7nd0zl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mb7qshu8a1pwx7nd0zl.png" alt="End-to-end OAuth2/OIDC + JWT authorization flow through API Gateway to microservices" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  1️⃣ Client → IdP → API Gateway → Microservices (OAuth2/OIDC + JWT)
&lt;/h3&gt;

&lt;p&gt;A typical modern setup: &lt;strong&gt;browser/SPA/mobile client → API Gateway/BFF → dozens of microservices&lt;/strong&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  🔐 Authentication at IdP (SSO)
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;The client opens the frontend, which redirects the user to the IdP using &lt;strong&gt;Authorization Code Flow (OIDC)&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;The user logs in (password, MFA, etc.).
&lt;/li&gt;
&lt;li&gt;The IdP creates a session and returns an &lt;strong&gt;authorization code&lt;/strong&gt; to the client.
&lt;/li&gt;
&lt;li&gt;The client exchanges this code for an &lt;strong&gt;access token&lt;/strong&gt; (often JWT) and a &lt;strong&gt;refresh token&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  📥 Client → API Gateway
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The client sends requests to the API Gateway with:
&lt;code&gt;Authorization: Bearer &amp;lt;access_token&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The gateway validates:

&lt;ul&gt;
&lt;li&gt;JWT signature
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exp&lt;/code&gt;, &lt;code&gt;iss&lt;/code&gt;, &lt;code&gt;aud&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scopes&lt;/code&gt; / &lt;code&gt;roles&lt;/code&gt;
using the IdP’s &lt;strong&gt;JWKS endpoint&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  📡 Gateway → Microservices
&lt;/h4&gt;

&lt;p&gt;The gateway can either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Proxy the original JWT&lt;/strong&gt; downstream as-is, or
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issue a lightweight internal token&lt;/strong&gt; for services (token exchange to an internal JWT).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each microservice then either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validates the token itself; or
&lt;/li&gt;
&lt;li&gt;Trusts the gateway’s validation and uses the provided claims for authorization.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  🧩 Authorization Inside Microservices
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Basic (RBAC)&lt;/strong&gt;: Check roles/scopes from the token (&lt;code&gt;admin&lt;/code&gt;, &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;order.read&lt;/code&gt;, &lt;code&gt;order.write&lt;/code&gt;, etc.).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced (ABAC / policy-based)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Fetch additional data from other services (&lt;strong&gt;fetch/replicate&lt;/strong&gt; patterns).
&lt;/li&gt;
&lt;li&gt;Delegate the decision to a &lt;strong&gt;centralized Authorization service&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  2️⃣ Service-to-Service (S2S) Authorization
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Pattern 1: Propagation (User Token Forwarding)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Service A receives a request with the &lt;strong&gt;user’s JWT&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;When calling Service B, A forwards the same JWT.
&lt;/li&gt;
&lt;li&gt;Service B validates the token and checks the user’s permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use when&lt;/strong&gt; you need &lt;strong&gt;end-to-end user identity&lt;/strong&gt; across services.&lt;/p&gt;


&lt;h4&gt;
  
  
  Pattern 2: Service Account / Client Credentials
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Each service gets its own &lt;code&gt;client_id&lt;/code&gt; / &lt;code&gt;client_secret&lt;/code&gt; / certificate.
&lt;/li&gt;
&lt;li&gt;A service uses &lt;strong&gt;Client Credentials Flow&lt;/strong&gt; to obtain a &lt;strong&gt;machine-to-machine token&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;It then calls other services &lt;strong&gt;on its own behalf&lt;/strong&gt;, not the user’s.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use when&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is no end-user (batch jobs, schedulers), or
&lt;/li&gt;
&lt;li&gt;You need service-level permissions (e.g. “BillingService can call PaymentService”).&lt;/li&gt;
&lt;/ul&gt;


&lt;h4&gt;
  
  
  Pattern 3: mTLS in the Mesh
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Inside the mesh, each pod/SAN is identified by a &lt;strong&gt;certificate&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Mesh policies (&lt;code&gt;AuthorizationPolicy&lt;/code&gt;, etc.) define &lt;strong&gt;which service can call which&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;User identity can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be propagated in &lt;strong&gt;JWT alongside mTLS&lt;/strong&gt;, or
&lt;/li&gt;
&lt;li&gt;Be omitted completely for &lt;strong&gt;batch/technical processes&lt;/strong&gt; where no end-user is involved.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  ⚙️ Resilience: How Painful Are Failures?
&lt;/h2&gt;

&lt;p&gt;Let’s look at failure points for each approach. Key chain links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IdP/Auth service&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Gateway&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session store / PKI / mesh control plane&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Individual microservice&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  1️⃣ OAuth2/OIDC + JWT
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;IdP/Auth service down:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New logins and refresh token flows stop working
&lt;/li&gt;
&lt;li&gt;Existing access tokens &lt;strong&gt;continue to be accepted&lt;/strong&gt; until they expire
&lt;/li&gt;
&lt;li&gt;Pain is &lt;strong&gt;delayed&lt;/strong&gt; — the system degrades as tokens expire&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;API Gateway down:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;External clients almost always &lt;strong&gt;lose access completely&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Pain is &lt;strong&gt;acute&lt;/strong&gt;, so the gateway must be scaled and protected aggressively&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Single microservice down:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only that service’s functionality breaks
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;authorization mechanism as a whole still works&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  2️⃣ JWT Without a Central IdP
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;strong&gt;each service validates JWT locally&lt;/strong&gt; using cached keys:

&lt;ul&gt;
&lt;li&gt;Auth service outage only affects &lt;strong&gt;new logins / key rotation&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If services call Auth service on every validation (&lt;strong&gt;fetch strategy&lt;/strong&gt;):

&lt;ul&gt;
&lt;li&gt;Auth service outage makes &lt;strong&gt;all requests unauthenticated/unauthorized&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2026 recommendation&lt;/strong&gt;: avoid hard runtime dependency on the Auth service; &lt;strong&gt;always validate JWT locally&lt;/strong&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  3️⃣ Sessions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Session store (Redis, SQL) down:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All sessions become unavailable → users are massively logged out
&lt;/li&gt;
&lt;li&gt;Pain is usually &lt;strong&gt;maximal&lt;/strong&gt;, since sessions are central shared state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Auth service that creates sessions down:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New logins are impossible
&lt;/li&gt;
&lt;li&gt;Existing sessions keep working as long as the session store is alive&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  4️⃣ API Keys
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Key management service down:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can’t issue/revoke keys
&lt;/li&gt;
&lt;li&gt;Existing keys &lt;strong&gt;continue to work&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Gateway down:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same as other schemes: very painful, as it’s usually the &lt;strong&gt;single entry point&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  5️⃣ mTLS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Control plane / PKI / mesh down:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existing connections may work for a while
&lt;/li&gt;
&lt;li&gt;New certificates aren’t issued, old ones aren’t rotated
&lt;/li&gt;
&lt;li&gt;As certificates expire, more and more S2S calls start failing with TLS errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mesh policy misconfig / incompatible mTLS config:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can instantly &lt;strong&gt;break a large portion of internal traffic&lt;/strong&gt;
### 6️⃣ Resilience Summary Table&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Central Component Failure (IdP/Auth/Session/PKI)&lt;/th&gt;
&lt;th&gt;Pain Level&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OAuth2 + JWT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No new logins/refresh, existing tokens work until &lt;code&gt;exp&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Delayed, manageable&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JWT (local validation)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No new tokens issued, existing tokens keep working&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Delayed&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JWT (fetch to Auth)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Auth service unavailable → all validations fail&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Acute, often critical&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sessions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Session store down → mass logout&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Very painful&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cannot manage keys, but existing keys still work&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Usually moderate&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;mTLS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PKI/mesh down → gradual degradation of S2S traffic as certs expire&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Growing, depends on DevOps quality&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  🧩 Authentication &amp;amp; Authorization Patterns in Microservices
&lt;/h2&gt;

&lt;p&gt;Patterns are &lt;strong&gt;proven architectural solutions&lt;/strong&gt; to recurring authorization problems. They help deal with &lt;strong&gt;distributed data&lt;/strong&gt;, &lt;strong&gt;scale&lt;/strong&gt;, and &lt;strong&gt;complexity&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;As of 2026, the focus is on combining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OAuth2/JWT for users&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;mTLS for service-to-service traffic&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delegated authorization&lt;/strong&gt; for complex permissions&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  1️⃣ API Gateway / BFF as the Authentication Entry Point
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
An &lt;strong&gt;API Gateway&lt;/strong&gt; (or &lt;strong&gt;Backend for Frontend, BFF&lt;/strong&gt;) is the &lt;strong&gt;single entry point&lt;/strong&gt; where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication happens
&lt;/li&gt;
&lt;li&gt;Coarse-grained authorization is enforced (basic checks: roles, scopes)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The token is then forwarded to microservices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Client → Gateway&lt;/strong&gt; (with credentials or an existing token)
&lt;/li&gt;
&lt;li&gt;The gateway:

&lt;ul&gt;
&lt;li&gt;Validates the token
&lt;/li&gt;
&lt;li&gt;Checks roles/scopes
&lt;/li&gt;
&lt;li&gt;Enriches the request with &lt;strong&gt;user context&lt;/strong&gt; (headers/claims)
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gateway → Microservices&lt;/strong&gt; (with token or extracted claims)
&lt;/li&gt;
&lt;li&gt;Microservices perform &lt;strong&gt;fine-grained authorization&lt;/strong&gt; using their own data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single, well-defined &lt;strong&gt;entry point&lt;/strong&gt; for external clients
&lt;/li&gt;
&lt;li&gt;Per-tenant isolation, rate limiting, traffic management
&lt;/li&gt;
&lt;li&gt;Minimizes duplicated auth logic across services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gateway becomes a &lt;strong&gt;single point of failure&lt;/strong&gt; → must be highly available
&lt;/li&gt;
&lt;li&gt;Potential tight coupling if services depend heavily on the gateway’s claim format&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ocelot&lt;/strong&gt; / &lt;strong&gt;YARP&lt;/strong&gt; (for ASP.NET)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kong&lt;/strong&gt;, &lt;strong&gt;KrakenD&lt;/strong&gt;, &lt;strong&gt;NGINX&lt;/strong&gt;, &lt;strong&gt;Traefik&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  2️⃣ Token Propagation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The &lt;strong&gt;user’s token (JWT)&lt;/strong&gt; is passed from the client through the gateway to &lt;strong&gt;every microservice in the call chain&lt;/strong&gt;. Each service validates the token and enforces its own access rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variants:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;End-user propagation&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
The original user JWT is propagated &lt;strong&gt;end-to-end&lt;/strong&gt; through all services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Service token exchange&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
The gateway exchanges the user JWT for a &lt;strong&gt;service token&lt;/strong&gt;, which is then used internally.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;End-to-end user identity&lt;/strong&gt; available in every microservice
&lt;/li&gt;
&lt;li&gt;Each service can &lt;strong&gt;independently&lt;/strong&gt; enforce its own authorization rules (RBAC/ABAC)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token is validated &lt;strong&gt;in every service&lt;/strong&gt; (CPU overhead)
&lt;/li&gt;
&lt;li&gt;Large tokens (many claims) increase request size across the entire call chain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User-centric flows where you need &lt;strong&gt;full user context&lt;/strong&gt; in downstream services
&lt;/li&gt;
&lt;li&gt;Scenarios requiring &lt;strong&gt;per-resource&lt;/strong&gt; checks (RBAC/ABAC) based on user identity and attributes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3️⃣ Service Account / Client Credentials
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
For &lt;strong&gt;service-to-service (S2S)&lt;/strong&gt; calls, each service has its own &lt;code&gt;client_id&lt;/code&gt; / &lt;code&gt;client_secret&lt;/code&gt; and obtains a token via the &lt;strong&gt;Client Credentials Flow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Service A → IdP&lt;/strong&gt; with &lt;code&gt;client_id&lt;/code&gt; / &lt;code&gt;client_secret&lt;/code&gt; → receives a &lt;strong&gt;service JWT&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service A → Service B&lt;/strong&gt; using this token in the &lt;code&gt;Authorization&lt;/code&gt; header
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service B&lt;/strong&gt; validates the token and checks the &lt;strong&gt;permissions of Service A&lt;/strong&gt; (not the end-user)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Permissions are &lt;strong&gt;assigned to services&lt;/strong&gt;, not users
&lt;/li&gt;
&lt;li&gt;Well-suited for &lt;strong&gt;batch jobs&lt;/strong&gt;, background workers, daemons&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No &lt;strong&gt;end-user context&lt;/strong&gt; (often needs to be combined with token propagation)
&lt;/li&gt;
&lt;li&gt;Storing secrets in services introduces &lt;strong&gt;secrets management risk&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Background jobs, schedulers, cron
&lt;/li&gt;
&lt;li&gt;Integrations between services where &lt;strong&gt;no end-user&lt;/strong&gt; is directly involved&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  4️⃣ Authorization Patterns with Distributed Data (4 Strategies)
&lt;/h3&gt;

&lt;p&gt;From Chris Richardson (microservices.io): used for &lt;strong&gt;fine-grained authorization&lt;/strong&gt; when permissions are spread across multiple services.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Provide&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All required claims (roles, tenant, relationships) are embedded into the JWT by IdP&lt;/td&gt;
&lt;td&gt;Fast, fully stateless&lt;/td&gt;
&lt;td&gt;JWT becomes huge, revocation is hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fetch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Service fetches permissions from other services on each request (REST/gRPC)&lt;/td&gt;
&lt;td&gt;Always up-to-date data&lt;/td&gt;
&lt;td&gt;Latency (N+1 calls), coupling to others&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Replicate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Service keeps a local replica of foreign data (events/CQRS-based sync)&lt;/td&gt;
&lt;td&gt;Fast local checks&lt;/td&gt;
&lt;td&gt;Eventual consistency, complex replication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delegate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Authorization is delegated to an external Authorization Service (OPA, Cerbos, Oso)&lt;/td&gt;
&lt;td&gt;Centralized policies, handles complex logic&lt;/td&gt;
&lt;td&gt;Dependency on AuthZ service, added latency&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Delegate example:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A microservice calls the AuthZ service:&lt;/p&gt;

&lt;p&gt;isAllowed(user=alice, action=delete, resource=order123) → YES/NO&lt;br&gt;
The AuthZ service returns yes or no, and the microservice enforces the result.&lt;/p&gt;

&lt;p&gt;5️⃣ Edge vs Service-Level Authorization&lt;br&gt;
Description:&lt;br&gt;
Two-layer authorization model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Edge (Gateway): Coarse-grained checks: roles, scopes, rate limiting, tenant isolation&lt;/li&gt;
&lt;li&gt;Service Level:Fine-grained checks: concrete permissions on specific resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defense in depth — multiple layers of protection&lt;/li&gt;
&lt;li&gt;The gateway filters out most invalid/unauthorized requests early&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplication of checks between gateway and services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For all public APIs, Internet-facing systems, and multi-tenant platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;6️⃣ Centralized Authorization Service&lt;br&gt;
Description:&lt;br&gt;
A separate service stores policies (e.g. Rego/OPA, Cedar/Cerbos) and answers questions like: allow?(user, action, resource) → true/false&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can handle complex logic (ABAC, ReBAC, tenant isolation) in one place&lt;/li&gt;
&lt;li&gt;Supports auditing, A/B testing of policies, GitOps workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds latency to each authorization decision&lt;/li&gt;
&lt;li&gt;Another single point of failure if not deployed in HA&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enterprise systems with thousands of rules and users&lt;/li&gt;
&lt;li&gt;Regulated industries where auditability and consistency of policies are critical&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  🔁 Delegate Pattern (Authorization Delegation) + OPA
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1mntap4ciobnncbwl3n4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1mntap4ciobnncbwl3n4.png" alt="Delegate Pattern (Authorization Delegation) + OPA" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Theory: The Distributed Permissions Problem
&lt;/h4&gt;

&lt;p&gt;In a microservices architecture, &lt;strong&gt;permissions for a single resource are often spread across multiple services&lt;/strong&gt;. For example, user &lt;strong&gt;Alice&lt;/strong&gt; might be allowed to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Read her own orders (&lt;strong&gt;Order Service&lt;/strong&gt;)
&lt;/li&gt;
&lt;li&gt;✅ Create payments for her own orders (&lt;strong&gt;Payment Service&lt;/strong&gt;)
&lt;/li&gt;
&lt;li&gt;❌ Modify other users’ orders (&lt;strong&gt;Order Service&lt;/strong&gt;)
&lt;/li&gt;
&lt;li&gt;✅ As a manager, see her team’s orders (&lt;strong&gt;User Service + Order Service&lt;/strong&gt; combined)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A JWT can carry only &lt;strong&gt;static claims&lt;/strong&gt; (roles, groups, basic attributes) — it does &lt;strong&gt;not&lt;/strong&gt; know about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Concrete relationships (owner of this order, member of this team)
&lt;/li&gt;
&lt;li&gt;Dynamic state (order status, project membership, org structure changes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The microservice &lt;strong&gt;delegates&lt;/strong&gt; the authorization decision to an external &lt;strong&gt;Authorization Service&lt;/strong&gt; that knows all the rules and data needed.&lt;/p&gt;


&lt;h4&gt;
  
  
  How the Delegate Pattern Works
&lt;/h4&gt;

&lt;p&gt;Flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Client → Order Service&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;DELETE /orders/123&lt;br&gt;
   Authorization: Bearer &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Order Service extracts key claims from the JWT:
{
"userId": "alice",
"roles": ["user"],
"tenant": "acme"
}
3.Order Service → AuthZ Service (delegated check):
POST /authorize
Content-Type: application/json&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;{&lt;br&gt;
  "principal": { "id": "alice", "roles": ["user"] },&lt;br&gt;
  "action": "delete",&lt;br&gt;
  "resource": { "type": "order", "id": "123" },&lt;br&gt;
  "context": { "tenant": "acme" }&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AuthZ Service → response:
{
"allow": true,
"reason": "user is owner of order"
}
or
{
"allow": false,
"reason": "user is not owner and not manager"
}&lt;/li&gt;
&lt;li&gt;Order Service either performs or rejects the operation based on the AuthZ decision.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  OPA (Open Policy Agent) as a Delegate Implementation
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;OPA&lt;/strong&gt; is a &lt;strong&gt;Policy-as-Code engine&lt;/strong&gt; that evaluates authorization policies written in the &lt;strong&gt;Rego&lt;/strong&gt; language.&lt;/p&gt;
&lt;h4&gt;
  
  
  ✅ Benefits of Delegate + OPA
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Complex logic&lt;/strong&gt;: Handles ABAC, ReBAC, tenant isolation, workflow-dependent rules
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Centralized policies&lt;/strong&gt;: One Rego policy set used by many services
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Auditability&lt;/strong&gt;: You can log every authorization decision
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;A/B testing of policies&lt;/strong&gt;: Roll out policy changes without redeploying services
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;GitOps-friendly&lt;/strong&gt;: Policies live in Git and are deployed via CI/CD
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Polyglot support&lt;/strong&gt;: OPA can be used from Go, Java, .NET, Node and more&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  ❌ Drawbacks
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Latency&lt;/strong&gt;: Adds ~50–200 ms per authorization call over the network
&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Single point of failure&lt;/strong&gt;: OPA must run in a highly available setup
&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Complexity&lt;/strong&gt;: Rego is a new language the team has to learn
&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Data synchronization&lt;/strong&gt;: Business data must be synced or exposed to OPA for decisions&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  🔁 Alternatives to OPA
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cerbos&lt;/strong&gt; — managed/embedded policy engine, often simpler to integrate
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenFGA&lt;/strong&gt; (Google Zanzibar model) — ReBAC (relationships: &lt;code&gt;user → document → viewer&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permify&lt;/strong&gt; — Zanzibar-style auth on PostgreSQL
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Oso&lt;/strong&gt; — policy language and engine with Python/Rust SDKs&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  🧭 When to Use the Delegate Pattern
&lt;/h4&gt;

&lt;p&gt;Use Delegate + Policy-as-Code when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ You have &lt;strong&gt;10+ microservices&lt;/strong&gt; with &lt;strong&gt;non-trivial permissions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ You are building a &lt;strong&gt;multi-tenant&lt;/strong&gt; platform
&lt;/li&gt;
&lt;li&gt;✅ You work in regulated industries (fintech, healthcare, gov)
&lt;/li&gt;
&lt;li&gt;✅ You need strong &lt;strong&gt;audit/compliance&lt;/strong&gt; guarantees
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ You have simple CRUD apps with basic &lt;strong&gt;role-based&lt;/strong&gt; access only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2026 status&lt;/strong&gt;: Policy-as-Code (OPA/Cerbos) is considered an &lt;strong&gt;enterprise standard&lt;/strong&gt;, especially in environments using &lt;strong&gt;GitOps&lt;/strong&gt; and &lt;strong&gt;service meshes&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  7️⃣ 2026 “Golden Standard”: Layered Approach
&lt;/h2&gt;
&lt;h3&gt;
  
  
  High-Level Architecture
&lt;/h3&gt;

&lt;p&gt;External Clients&lt;br&gt;
      ↓&lt;br&gt;
API Gateway / BFF&lt;br&gt;
  (OAuth2/OIDC + JWT validation, coarse-grained auth)&lt;br&gt;
      ↓&lt;br&gt;
Microservices&lt;br&gt;
  (token propagation or service tokens)&lt;br&gt;
      ↕&lt;br&gt;
Microservice ↔ Microservice&lt;br&gt;
  (mTLS in a service mesh + fine-grained auth)&lt;br&gt;
External clients → API Gateway&lt;/p&gt;

&lt;p&gt;Gateway handles OAuth2/OIDC, validates JWTs, and enforces coarse-grained authorization (roles, scopes, tenant).&lt;/p&gt;

&lt;p&gt;Gateway → Microservices&lt;/p&gt;

&lt;p&gt;Uses token propagation (forwarding user JWT) or service tokens (Client Credentials) for internal calls.&lt;/p&gt;

&lt;p&gt;Microservice ↔ Microservice&lt;/p&gt;

&lt;p&gt;Runs over mTLS inside a service mesh, with fine-grained authorization:&lt;/p&gt;

&lt;p&gt;Local RBAC/ABAC in each service, or&lt;/p&gt;

&lt;p&gt;Delegated checks via a centralized AuthZ service (OPA/Cerbos/etc.).&lt;/p&gt;

&lt;p&gt;✅ Pros&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Covers all major scenarios (user-facing, S2S, batch, partners)&lt;/li&gt;
&lt;li&gt;Enables zero-trust networking inside the cluster&lt;/li&gt;
&lt;li&gt;Scales well horizontally with stateless tokens and mesh-based security&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❌ Cons&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased infrastructure complexity&lt;/li&gt;
&lt;li&gt;Requires a mature DevOps/SRE culture and good observability
This is effectively what companies like Netflix, Uber, DoorDash and other large-scale players run in production today.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💻 Minimal C# Example (ASP.NET Core + JWT)&lt;br&gt;
Below is a minimal JWT authentication setup for a microservice built with ASP.NET Core 8/10 Web API (acting as an OAuth2 resource server behind a gateway).&lt;/p&gt;
&lt;h3&gt;
  
  
  Minimal JWT Setup in ASP.NET Core (8/10)
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1️⃣ &lt;code&gt;Program.cs&lt;/code&gt; — JWT Bearer Configuration
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Authentication.JwtBearer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.IdentityModel.Tokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultAuthenticateScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JwtBearerDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultChallengeScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JwtBearerDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJwtBearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// URL of your IdP (e.g. https://idp.example.com)&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Jwt:Authority"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="c1"&gt;// Audience = this API's identifier (aud claim)&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Audience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Jwt:Audience"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TokenValidationParameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;TokenValidationParameters&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ValidateIssuer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ValidateAudience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ValidateLifetime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ValidateIssuerSigningKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

            &lt;span class="c1"&gt;// If you are not using Authority/JWKS and rely on a symmetric key:&lt;/span&gt;
            &lt;span class="n"&gt;IssuerSigningKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SymmetricSecurityKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Jwt:Key"&lt;/span&gt;&lt;span class="p"&gt;]!))&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRouting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthentication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;2️⃣ Controller with Role-Based Authorization&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Authorization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrdersController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Any authenticated user&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Authorize&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;GetMyOrders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindFirst&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Load orders by userId...&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* orders */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Only users with the "admin" role&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id:int}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Roles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;DeleteOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Delete order...&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NoContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a real production system, these APIs would typically sit behind an API Gateway, which:&lt;/p&gt;

&lt;p&gt;Validates tokens at the edge&lt;/p&gt;

&lt;p&gt;Optionally enriches requests with user context&lt;/p&gt;

&lt;p&gt;Forwards the token (or an internal token) to microservices&lt;/p&gt;

&lt;p&gt;The tokens themselves are issued by an IdP such as Duende IdentityServer, Auth0, Azure AD B2C, or Keycloak.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Which Approach to Use When (Practical Guidance)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Decision Table for 2026
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommended Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SPA / mobile client + many microservices&lt;/td&gt;
&lt;td&gt;OAuth2/OIDC + JWT via IdP and API Gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public REST API for external clients&lt;/td&gt;
&lt;td&gt;OAuth2 (auth code / client credentials) + JWT or opaque tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal service-to-service calls&lt;/td&gt;
&lt;td&gt;mTLS in a service mesh + either user JWT propagation or service tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small web app / semi-monolith&lt;/td&gt;
&lt;td&gt;Sessions (cookies + session store), or JWT if microservices migration is planned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple batch / CLI integrations&lt;/td&gt;
&lt;td&gt;API Keys or OAuth2 Client Credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fintech, government, critical infrastructure&lt;/td&gt;
&lt;td&gt;Combo: OIDC+JWT for users, mTLS for services, short-lived tokens, MFA, audit logging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For &lt;strong&gt;modern microservices with external clients&lt;/strong&gt;, your &lt;strong&gt;default choice&lt;/strong&gt; should be:&lt;br&gt;&lt;br&gt;
&lt;code&gt;IdP + OAuth2/OIDC + JWT + API Gateway&lt;/code&gt;, with &lt;strong&gt;mTLS and short-lived tokens&lt;/strong&gt; inside the cluster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep &lt;strong&gt;sessions&lt;/strong&gt; for &lt;strong&gt;simple / legacy web apps&lt;/strong&gt; and &lt;strong&gt;internal admin panels&lt;/strong&gt;, where microservices aren’t really needed yet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;strong&gt;API keys&lt;/strong&gt; &lt;strong&gt;sparingly&lt;/strong&gt;, for those services and scripts where a full OAuth2 setup would be overkill.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When designing your solution, consider not only &lt;strong&gt;security&lt;/strong&gt;, but also &lt;strong&gt;resilience&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
how painful will it be if &lt;strong&gt;IdP&lt;/strong&gt;, &lt;strong&gt;gateway&lt;/strong&gt;, &lt;strong&gt;session store&lt;/strong&gt;, or &lt;strong&gt;PKI/mesh&lt;/strong&gt; go down?&lt;br&gt;&lt;br&gt;
In 2026, this is just as important a design criterion as developer convenience.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>microservices</category>
      <category>jwt</category>
      <category>ocelot</category>
    </item>
    <item>
      <title>Task Tracker Comparison 2025–2026: Which One for .NET/DevOps?з</title>
      <dc:creator>Татьяна Кузнецова</dc:creator>
      <pubDate>Thu, 29 Jan 2026 13:53:10 +0000</pubDate>
      <link>https://forem.com/belochka1-04/task-tracker-comparison-2025-2026-which-one-for-netdevopsz-32gh</link>
      <guid>https://forem.com/belochka1-04/task-tracker-comparison-2025-2026-which-one-for-netdevopsz-32gh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"Effective time management is key to developer and team productivity" — &lt;br&gt;
sounds official, but it perfectly captures project reality. Without a system, tasks blur, deadlines slip, and burnout creeps in: a huge chunk of time gets lost switching between tasks. Task trackers fix this — they visualize backlogs, prioritize (Eisenhower/MoSCoW), track time, and automate routine, boosting focus noticeably.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhydrraj9j2jzw3ovayn3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhydrraj9j2jzw3ovayn3.png" alt="Task Trackers" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Year after year trying different task trackers, I keep asking: which one's most convenient? Has anything better appeared? Based on 2025 user reviews and roundups (G2, Reddit, Habr, RU blogs), leaders are &lt;strong&gt;Asana, Jira, ClickUp, Trello, Monday.com&lt;/strong&gt; globally + &lt;strong&gt;YouGile, Yandex Tracker, Kaiten&lt;/strong&gt; in the Russian-speaking segment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Popularity in 2025
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Global:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ClickUp leads growth (often #1 in PM rankings).&lt;/li&gt;
&lt;li&gt;Jira dominates in IT/DevOps teams.&lt;/li&gt;
&lt;li&gt;Trello and Asana stay popular for their simplicity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Russia / RU segment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yandex Tracker, YouGile, Kaiten are frequently recommended for compliance, pricing, and self-hosting.&lt;/li&gt;
&lt;li&gt;Jira is still a go-to option in many dev teams.&lt;/li&gt;
&lt;li&gt;Habr articles often praise Russian tools for being on‑prem friendly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On Reddit you’ll often see: “ClickUp is all-in-one, Jira is for serious dev work.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features Comparison
&lt;/h2&gt;

&lt;p&gt;All of them cover the basics: Kanban, lists, deadlines, comments, and notifications. The real differences are in agile tooling, automation, AI, and integrations — that’s what affects time tracking, prioritization, and how painless your daily workflow feels.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Core Features&lt;/th&gt;
&lt;th&gt;Advanced Features&lt;/th&gt;
&lt;th&gt;Integrations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Asana&lt;/td&gt;
&lt;td&gt;Boards, lists&lt;/td&gt;
&lt;td&gt;Timeline, automations&lt;/td&gt;
&lt;td&gt;1000+ (Slack, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jira&lt;/td&gt;
&lt;td&gt;Backlogs, sprints&lt;/td&gt;
&lt;td&gt;Roadmaps, AI, advanced workflows&lt;/td&gt;
&lt;td&gt;GitHub, Bitbucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ClickUp&lt;/td&gt;
&lt;td&gt;Hierarchy (spaces, lists), views&lt;/td&gt;
&lt;td&gt;AI, time-tracking, goals&lt;/td&gt;
&lt;td&gt;1000+ via native/Zapier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trello&lt;/td&gt;
&lt;td&gt;Simple Kanban boards&lt;/td&gt;
&lt;td&gt;Power-ups, automations (Butler)&lt;/td&gt;
&lt;td&gt;Google, Slack, others&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monday.com&lt;/td&gt;
&lt;td&gt;Dashboards, boards&lt;/td&gt;
&lt;td&gt;CRM, Gantt, automation&lt;/td&gt;
&lt;td&gt;Zapier and many more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YouGile&lt;/td&gt;
&lt;td&gt;Kanban, built-in chats&lt;/td&gt;
&lt;td&gt;CRM-like features, messenger&lt;/td&gt;
&lt;td&gt;Telegram, 1C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yandex Tracker&lt;/td&gt;
&lt;td&gt;Agile boards, queues&lt;/td&gt;
&lt;td&gt;Deep Yandex ecosystem integration&lt;/td&gt;
&lt;td&gt;Mail, Wiki, Tracker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kaiten&lt;/td&gt;
&lt;td&gt;Kanban, checklists&lt;/td&gt;
&lt;td&gt;Automations, analytics&lt;/td&gt;
&lt;td&gt;Bitrix, Telegram&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd26wuydazobr2mue7qz4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd26wuydazobr2mue7qz4.png" alt="Features comparison" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing (/user/month, annual billing)
&lt;/h2&gt;

&lt;p&gt;Global tools mostly bill in USD, Russian tools — in RUB, often with self-host options.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important about Jira Data Center:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Atlassian is sunsetting Data Center: new sales for new customers end March 30, 2026, renewals for existing customers are allowed until March 30, 2028, with full end-of-life (effectively read-only / end of support) in March 2029.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Free Plan&lt;/th&gt;
&lt;th&gt;Premium (from, /user/mo)&lt;/th&gt;
&lt;th&gt;Self-host / On‑prem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Asana&lt;/td&gt;
&lt;td&gt;Up to 10 users&lt;/td&gt;
&lt;td&gt;Starter from $10.99&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jira&lt;/td&gt;
&lt;td&gt;Up to 10 users&lt;/td&gt;
&lt;td&gt;Standard from $9.05&lt;/td&gt;
&lt;td&gt;Data Center (EOL 2029)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ClickUp&lt;/td&gt;
&lt;td&gt;Unlimited users&lt;/td&gt;
&lt;td&gt;Unlimited from $7&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trello&lt;/td&gt;
&lt;td&gt;Unlimited users&lt;/td&gt;
&lt;td&gt;Standard from $5&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monday.com&lt;/td&gt;
&lt;td&gt;Up to 2 users&lt;/td&gt;
&lt;td&gt;Basic from $9&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YouGile&lt;/td&gt;
&lt;td&gt;Unlimited users&lt;/td&gt;
&lt;td&gt;from 396 RUB&lt;/td&gt;
&lt;td&gt;Yes, from ~695 RUB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yandex Tracker&lt;/td&gt;
&lt;td&gt;Up to 10 users&lt;/td&gt;
&lt;td&gt;~258–495 RUB&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kaiten&lt;/td&gt;
&lt;td&gt;Up to 10 users&lt;/td&gt;
&lt;td&gt;~580 RUB&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Mobile Apps (2025–2026)
&lt;/h2&gt;

&lt;p&gt;All these tools have iOS/Android apps with solid ratings, typically in the 4.5–4.8/5 range. For time management “on the go”, that matters a lot: it’s your way to check the board in the subway, move a ticket to “In Progress”, or close a bug from your phone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2lpto5r64fq7p3tx6h8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2lpto5r64fq7p3tx6h8.png" alt="Mobile task apps" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asana&lt;/strong&gt; — fast task creation and notifications, occasionally users mention sync glitches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jira&lt;/strong&gt; — good for viewing issues and commenting; advanced filtering and configuration are still more convenient on desktop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ClickUp&lt;/strong&gt; — powerful views on mobile, but can feel overwhelming for new users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trello&lt;/strong&gt; — probably the most touch-friendly Kanban; drag‑and‑drop just works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monday.com&lt;/strong&gt; — convenient dashboards and high-level monitoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YouGile&lt;/strong&gt; — intuitive chats + tasks in one app, rare freezes mentioned in reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yandex Tracker&lt;/strong&gt; — quick status updates and task creation, well-tuned for Yandex ecosystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kaiten&lt;/strong&gt; — full boards with offline support; good fit when you need Kanban everywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Role-Based Rankings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Developers / DevOps
&lt;/h3&gt;

&lt;p&gt;If you care about sprints, Git integration, and CI/CD visibility:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Jira&lt;/strong&gt; – strong agile tooling (sprints, backlogs, roadmaps) and first-class GitHub/Bitbucket integration.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yandex Tracker&lt;/strong&gt; – agile boards, self-host and MSSQL-friendly, good fit for Russian infrastructure.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ClickUp&lt;/strong&gt; – lots of views, built-in time tracking, AI for writing and summarizing.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kaiten&lt;/strong&gt; – Kanban-centric with automations, suitable for product and DevOps teams.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bitrix24&lt;/strong&gt; – heavy, but offers tasks plus CRM and comms in one place.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6iglec2vfh6f5vxj9rmb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6iglec2vfh6f5vxj9rmb.png" alt="Task trackers raiting" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Managers / Leads
&lt;/h3&gt;

&lt;p&gt;If you mostly watch progress, deadlines, and cross-team status:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Monday.com&lt;/strong&gt; – dashboards, Gantt, portfolios; easy to present to stakeholders.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asana&lt;/strong&gt; – timelines and portfolios for project roadmaps.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YouGile&lt;/strong&gt; – quick overviews plus team communication in one tool.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ClickUp&lt;/strong&gt; – goals, time tracking, and reporting, if you’re ready for the learning curve.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trello&lt;/strong&gt; – simple visual overview for smaller teams and projects.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Freelancers / Small Teams
&lt;/h3&gt;

&lt;p&gt;When you don’t want to overcomplicate it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trello&lt;/strong&gt; – unlimited free, extremely simple Kanban.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ClickUp&lt;/strong&gt; – generous free tier and “everything in one place” approach.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YouGile&lt;/strong&gt; – chat + tasks, good for small Russian-speaking teams.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Communication‑Heavy Teams (IoT, music apps, etc.)
&lt;/h3&gt;

&lt;p&gt;If your team lives in chat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;YouGile&lt;/strong&gt; – built-in messenger tightly linked with tasks.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bitrix24&lt;/strong&gt; – telephony, chats, tasks, CRM in one ecosystem.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yandex Tracker&lt;/strong&gt; – works well together with Yandex Mail, Wiki, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recommendations for .NET/DevOps in Russia
&lt;/h2&gt;

&lt;p&gt;If you’re in the .NET/DevOps world:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jira Cloud&lt;/strong&gt; is still a very solid choice for GitHub integration and classic agile. Keep in mind the Data Center end-of-life if you were considering self-hosting.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yandex Tracker&lt;/strong&gt; and &lt;strong&gt;Kaiten&lt;/strong&gt; are great local self-host options, friendly to Russian infrastructure, with MSSQL and good mobile apps.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My practical suggestion: start with the free tiers and mobile apps of 2–3 tools that fit your stack and team size. Run a real sprint or a small project in each — you’ll feel in a week which tracker actually saves you time instead of stealing it.&lt;/p&gt;

&lt;p&gt;In my personal ranking, Jira still confidently holds the first place.&lt;br&gt;
What about you — which task tracker do you enjoy using the most, and why?&lt;br&gt;
&lt;em&gt;This overview reflects the state of tools and pricing as of early 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>productivity</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Smart Kitchen Shelf: .NET + ESP32 + AI for Full Inventory Automation. Part 1</title>
      <dc:creator>Татьяна Кузнецова</dc:creator>
      <pubDate>Tue, 20 Jan 2026 13:39:11 +0000</pubDate>
      <link>https://forem.com/belochka1-04/smart-kitchen-shelf-net-esp32-ai-for-full-inventory-automation-part-1-36be</link>
      <guid>https://forem.com/belochka1-04/smart-kitchen-shelf-net-esp32-ai-for-full-inventory-automation-part-1-36be</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57wrz82w4wjhc1cpau5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57wrz82w4wjhc1cpau5f.png" alt="SmartHome" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With AI entering our lives, home automation and smart home systems have become much simpler and accessible to everyone. I've long wanted to create a system that would take most of the product management duties off my hands. This routine task eats up so much time that could be spent more productively.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea: From Total Chaos to Automation
&lt;/h2&gt;

&lt;p&gt;The ultimate goal is a system that automatically tracks all products in your home, knows their composition and expiration dates, auto-orders what's running low, and ideally even removes expired items (though that's still science fiction, as is automatic fridge organization). It should suggest menus based on family diets, allergies, taste preferences, and eating history.&lt;/p&gt;

&lt;p&gt;But we have to start somewhere! For a long time, figuring out products, expiration dates, and composition without manual entry was a problem. One solution was barcode scanning, but... Different barcode databases, not always accessible, barcodes don't reveal composition, and &lt;strong&gt;bringing&lt;/strong&gt; every product to the scanner isn't real automation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But AI changes everything!&lt;/strong&gt; Now we can recognize objects in photos, instantly find manufacturer info, composition, and there are even systems that find optimal prices! Life is getting better! That's what I was thinking just yesterday.&lt;/p&gt;

&lt;p&gt;My immediate goal is a test project monitoring products in &lt;strong&gt;one cabinet first&lt;/strong&gt;, then expanding to others and the fridge once debugged.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We're Building On
&lt;/h2&gt;

&lt;p&gt;Kitchen food cabinet &lt;strong&gt;80×40×60 cm&lt;/strong&gt;. &lt;strong&gt;Glass shelves&lt;/strong&gt; (this is important).&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Foundation: ESP32-S3 + OV5640 Camera (No Tags Needed)
&lt;/h2&gt;

&lt;p&gt;For full hands-free automation, I'm using &lt;strong&gt;ESP32-S3 with OV5640 camera module&lt;/strong&gt; (5MP, &lt;strong&gt;fisheye lens 160-170°&lt;/strong&gt; for complete shelf coverage).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Camera placement&lt;/strong&gt;: Horizontally mounted above each glass shelf (20-30 cm above, 45° downward angle for full row visibility to the back wall), attached with double-sided tape or clips — this way they see through the glass without product obstruction (height up to 20 cm).&lt;/p&gt;

&lt;p&gt;When the door opens (&lt;strong&gt;GPIO sensor&lt;/strong&gt;), ESP captures a photo in 1 second, and AI (&lt;strong&gt;TensorFlow Lite Micro or YOLOv8-lite on edge&lt;/strong&gt;) recognizes products: grains, cans, spices by shape, color, packaging. Accuracy after calibration (10 photos/product) — &lt;strong&gt;92-98%&lt;/strong&gt; for 50+ kitchen items, even with stacked rows.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One 60×40 cm shelf needs &lt;strong&gt;1 ESP32-S3 camera&lt;/strong&gt; (fisheye covers 100% without multiplexing), three shelves need &lt;strong&gt;3 modules&lt;/strong&gt; with WiFi bridge to central hub.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI extrapolates occluded areas (back rows), counts quantities, reads expiration dates from labels (&lt;strong&gt;OCR on ESP-IDF&lt;/strong&gt;). Data sent via &lt;strong&gt;MQTT to .NET backend&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I have Arduino — should I use it?&lt;/strong&gt; No need: ESP32-S3 is self-sufficient (WiFi, GPIO, camera, powerful CPU/PSRAM for AI), simpler and cheaper for IoT without extra boards. Arduino works only for sensor testing, but loses to camera+AI in performance and connectivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  .NET Backend + AI Agent Integration
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ASP.NET Core backend&lt;/strong&gt; receives photos from ESP32 (WiFi POST or MQTT), &lt;strong&gt;ML.NET&lt;/strong&gt; for local vision model or cloud YOLO API.&lt;/p&gt;

&lt;p&gt;C# agent (&lt;strong&gt;LangChain.NET&lt;/strong&gt;) analyzes inventory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates menus: &lt;em&gt;"Chicken, rice, carrots → pilaf for 4, 450 kcal/serving"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Considers family: allergies, tastes (spicy for husband, gluten-free for child)&lt;/li&gt;
&lt;li&gt;Auto-order: Ozon/Wildberries API integration for best prices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;API endpoint example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"analyze-shelf"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;AnalyzeShelf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IFormFile&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"yolov8_kitchen.zip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;prediction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreatePredictionEngine&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ImageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Prediction&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Process: product list, quantities, dates&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;KitchenAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;familyPrefs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;menu&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SuggestMeals&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;menu&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  💰 Cost Breakdown
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Price (RUB)&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ESP32-S3 + OV5640 &lt;strong&gt;fisheye&lt;/strong&gt; (x3)&lt;/td&gt;
&lt;td&gt;2344&lt;/td&gt;
&lt;td&gt;618 RUB/module + 200 RUB fisheye lens + 240 RUB shipping (or 900 RUB/unit on Ozon) [3]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 glass shelves&lt;/td&gt;
&lt;td&gt;2200&lt;/td&gt;
&lt;td&gt;Tempered 6-8mm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sensor + relay&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;GPIO auto-trigger&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5044&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Compact, 160° coverage without blind spots&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Prototype on &lt;strong&gt;ESP-IDF or MicroPython&lt;/strong&gt; ready in a weekend: GPIO for sensor, MQTT to .NET API, &lt;strong&gt;Telegram bot&lt;/strong&gt; for alerts (&lt;em&gt;"Milk gone, buy 2L"&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ESP32-S3 runs YOLO-lite onboard (50ms/frame)&lt;/strong&gt;, waiting for AliExpress parts — once they arrive, I'll assemble and write &lt;strong&gt;Part 2&lt;/strong&gt; with real tests and code.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Have You Tried This?
&lt;/h2&gt;

&lt;p&gt;Curious: &lt;strong&gt;has anyone built similar smart shelves with camera+AI fisheye?&lt;/strong&gt; Did you get it working in practice?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Share in comments&lt;/strong&gt; — experiences, repos, occlusion or accuracy issues!&lt;/p&gt;

&lt;p&gt;This isn't sci-fi, it's a &lt;strong&gt;real step toward household automation with .NET + ESP32 + AI&lt;/strong&gt;. Try it yourself — open source code (ESP32-CAM pantry on GitHub), hardware available on Ali.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Worth scaling to full kitchen?&lt;/strong&gt; 🚀[3]&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Part 1/2. Part 2 with real tests, code, and recognition accuracy — coming once cameras arrive from Ali!&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  dotnet #esp32 #ai #iot #smarthome #computervision #yolo #mlnet
&lt;/h1&gt;

</description>
      <category>iot</category>
      <category>dotnet</category>
      <category>esp32</category>
      <category>smarthome</category>
    </item>
    <item>
      <title>Don't pull the entire dump if you only need a small piece</title>
      <dc:creator>Татьяна Кузнецова</dc:creator>
      <pubDate>Sun, 11 Jan 2026 17:46:03 +0000</pubDate>
      <link>https://forem.com/belochka1-04/dont-pull-the-entire-dump-if-you-only-need-a-small-piece-2aka</link>
      <guid>https://forem.com/belochka1-04/dont-pull-the-entire-dump-if-you-only-need-a-small-piece-2aka</guid>
      <description>&lt;p&gt;In one of my recent projects for a Canadian company, I was tasked with generating fairly complex PDF reports in a distributed data system.[file:54]&lt;br&gt;&lt;br&gt;
The report itself was based on data from just &lt;strong&gt;three tables&lt;/strong&gt; – but those tables contained &lt;strong&gt;millions of rows&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The obvious problem: the database on dev/stage is huge.&lt;br&gt;&lt;br&gt;
Pulling a full backup (hundreds of GBs) to a local machine just to debug one reporting module is a great way to waste half a day and most of your SSD.&lt;/p&gt;
&lt;h2&gt;
  
  
  The naive approach: full backup or SSMS scripts
&lt;/h2&gt;

&lt;p&gt;Most teams start with one of these options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restore a &lt;strong&gt;full database backup&lt;/strong&gt; locally.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;SSMS → Generate Scripts (Schema + Data)&lt;/strong&gt; for the required tables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both options look reasonable on paper, but break down at scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A full backup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes hours to download and restore.&lt;/li&gt;
&lt;li&gt;Eats a massive chunk of disk space.&lt;/li&gt;
&lt;li&gt;Includes a lot of data you simply do not need for a specific test case.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SSMS “Generate Scripts” for tables with millions of rows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tries to produce a gigantic &lt;code&gt;INSERT&lt;/code&gt; script.&lt;/li&gt;
&lt;li&gt;Frequently freezes or dies with OutOfMemory.&lt;/li&gt;
&lt;li&gt;Even if the script is generated, executing it can take many hours.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At some point it becomes clear: &lt;strong&gt;we don’t need the whole mountain – just a slice of rock&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  A better approach: surgical extraction with BCP
&lt;/h2&gt;

&lt;p&gt;For SQL Server, my go‑to tool here is &lt;code&gt;bcp&lt;/code&gt; (Bulk Copy Program).&lt;br&gt;&lt;br&gt;
Instead of moving an entire 200+ GB database around, I extract &lt;strong&gt;only the data slices needed for debugging&lt;/strong&gt; and recreate them locally.&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep the &lt;strong&gt;schema&lt;/strong&gt; in source control (migrations, DDL scripts).&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;BCP&lt;/strong&gt; to move &lt;strong&gt;only the relevant data&lt;/strong&gt; between environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Why BCP beats SSMS for this use case
&lt;/h3&gt;

&lt;p&gt;BCP is not new or flashy, but it shines when you need to move lots of rows quickly.&lt;/p&gt;

&lt;p&gt;Key advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Speed&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
BCP works with a binary stream in native format.&lt;br&gt;&lt;br&gt;
There’s no SQL text to parse, no giant &lt;code&gt;INSERT&lt;/code&gt; statements.&lt;br&gt;&lt;br&gt;
Millions of rows can be exported/imported in minutes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stability&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A console utility does not hang on a heavy UI operation or choke while rendering a huge script file.&lt;br&gt;&lt;br&gt;
It either runs fast or fails with a clear error.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can export:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A whole table, or&lt;/li&gt;
&lt;li&gt;The result of an arbitrary &lt;code&gt;SELECT&lt;/code&gt; with joins and filters.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Export &lt;strong&gt;just the subset&lt;/strong&gt; of data you need for a given test scenario.&lt;/li&gt;
&lt;li&gt;Exclude sensitive columns or mask them directly in the SELECT.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Practical workflow: from prod slice to local debug
&lt;/h2&gt;

&lt;p&gt;In the PDF-reporting case, the flow looked like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Prepare the schema locally&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Apply migrations or run your DDL scripts to create the same tables locally.&lt;/li&gt;
&lt;li&gt;No data yet, just structure.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Export the data slice from upstream environment&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example (conceptual):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   bcp &lt;span class="s2"&gt;"SELECT c.Id, c.Name, o.Id, o.Date, o.Total
        FROM dbo.Customers c
        JOIN dbo.Orders o ON o.CustomerId = c.Id
        WHERE o.Date &amp;gt;= '2025-01-01'"&lt;/span&gt;
        queryout orders_customers.dat &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-S&lt;/span&gt; your-server &lt;span class="nt"&gt;-T&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
Notes:&lt;/p&gt;

&lt;p&gt;queryout lets you export the result of a SELECT, not just a whole table.&lt;/p&gt;

&lt;p&gt;-n uses native (binary) format — fast and compact.&lt;/p&gt;

&lt;p&gt;-T uses trusted connection; replace with -U/-P if needed.&lt;/p&gt;

&lt;p&gt;Import into local database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bcp dbo.OrdersCustomers &lt;span class="k"&gt;in &lt;/span&gt;orders_customers.dat &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-S&lt;/span&gt; localhost &lt;span class="nt"&gt;-T&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;/p&gt;

&lt;p&gt;You can split this into several files (per table) if it better matches your local schema.&lt;/p&gt;

&lt;p&gt;Debug with realistic volumes&lt;/p&gt;

&lt;p&gt;Now your local database contains:&lt;/p&gt;

&lt;p&gt;The relevant tables.&lt;/p&gt;

&lt;p&gt;The right data shape.&lt;/p&gt;

&lt;p&gt;Realistic row counts (millions if needed).&lt;/p&gt;

&lt;p&gt;But you never had to restore the full 200+ GB backup.&lt;/p&gt;

&lt;p&gt;When you should consider this approach&lt;br&gt;
This pattern works especially well when:&lt;/p&gt;

&lt;p&gt;You have a huge SQL Server database in upper environments.&lt;/p&gt;

&lt;p&gt;You need realistic data for:&lt;/p&gt;

&lt;p&gt;Debugging complex business logic.&lt;/p&gt;

&lt;p&gt;Reproducing production-only bugs.&lt;/p&gt;

&lt;p&gt;Testing performance of reports or batch jobs.&lt;/p&gt;

&lt;p&gt;You don’t want to:&lt;/p&gt;

&lt;p&gt;Drag full backups around.&lt;/p&gt;

&lt;p&gt;Rely on fragile UI exports.&lt;/p&gt;

&lt;p&gt;Maintain a massive “test data” SQL script by hand.&lt;/p&gt;

&lt;p&gt;BCP is not a silver bullet, but in scenarios where:&lt;/p&gt;

&lt;p&gt;You know exactly which subset of data you need.&lt;/p&gt;

&lt;p&gt;The volumes are too big for SSMS scripting.&lt;/p&gt;

&lt;p&gt;You already have schema migrations in place.&lt;/p&gt;

&lt;p&gt;…it can save you many hours per week and make local debugging much more pleasant.&lt;/p&gt;

&lt;p&gt;How do you seed your local databases from large upstream environments?&lt;br&gt;
Full backups, BACPACs, custom seeders, or something else?&lt;/p&gt;

&lt;p&gt;Let me know — always curious about real-world workflows around this problem.&lt;/p&gt;

</description>
      <category>sqlserver</category>
      <category>dotnet</category>
      <category>backend</category>
      <category>devops</category>
    </item>
    <item>
      <title>From 3+ Days to 3.8 Hours: Scaling a .NET CSV Importer for SQL Server</title>
      <dc:creator>Татьяна Кузнецова</dc:creator>
      <pubDate>Sun, 04 Jan 2026 15:29:47 +0000</pubDate>
      <link>https://forem.com/belochka1-04/from-3-days-to-38-hours-scaling-a-net-csv-importer-for-sql-server-ge1</link>
      <guid>https://forem.com/belochka1-04/from-3-days-to-38-hours-scaling-a-net-csv-importer-for-sql-server-ge1</guid>
      <description>&lt;p&gt;&lt;em&gt;How changing requirements forced a 24x performance boost in a data pipeline.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Good Enough" Solution That Wasn't
&lt;/h2&gt;

&lt;p&gt;Every project has that one task: "Just load this massive CSV into the database once, and we're done." That was my starting point. I had a 40 GB CSV file containing complex nested JSON structures (models, diagrams, hotspots, elements) that needed to be parsed and saved into a normalized SQL Server schema.&lt;/p&gt;

&lt;p&gt;The first version of the importer was straightforward: read a row, parse the JSON, create entities, save to the DB. It worked, but it took &lt;strong&gt;over 3 days (~92 hours)&lt;/strong&gt; to finish. For a one-time migration, this was painful but acceptable. You launch it on Friday, and hopefully, it's done by Monday.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then the requirements changed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The business decided this wasn't a one-off event. We needed to load &lt;em&gt;several&lt;/em&gt; more files of similar size and potentially update them regularly. Suddenly, a 3-day runtime became a blocker. Loading a queue of files would take weeks, paralyzing analytics and development. The "naive" sequential importer was no longer just slow—it was unusable for the new workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Why Was It Slow?
&lt;/h2&gt;

&lt;p&gt;Parsing and inserting data sounds simple, but at scale (40 GB, millions of complex objects), the "standard" approach hits hard limits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Sequential Processing:&lt;/strong&gt; Reading lines and parsing JSON one by one left the CPU idle while waiting for the DB, and vice versa.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Database Round-Trips:&lt;/strong&gt; Saving entities individually (or in tiny groups) caused massive overhead. The database spent more time managing transactions and network calls than actually storing data.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Memory Pressure:&lt;/strong&gt; Loading full &lt;code&gt;JsonDocument&lt;/code&gt; objects for every row created huge GC pressure.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Fragility:&lt;/strong&gt; A single error after 2 days of processing could crash the whole pipeline, forcing a restart.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Solution: High-Performance Architecture
&lt;/h2&gt;

&lt;p&gt;To meet the new "multi-file" requirement, I redesigned the system to be parallel, batched, and resilient.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Controlled Parallelism with &lt;code&gt;SemaphoreSlim&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of a single thread, I implemented a producer-consumer pattern using &lt;code&gt;SemaphoreSlim&lt;/code&gt; to limit concurrency to &lt;strong&gt;8 parallel workers&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Why:&lt;/strong&gt; It saturates the CPU and DB connection pool just enough to be fast without choking the server. Unbounded parallelism (&lt;code&gt;Parallel.ForEach&lt;/code&gt;) would have killed the database performance.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Safety:&lt;/strong&gt; Each worker gets its own &lt;code&gt;DbContext&lt;/code&gt; via &lt;code&gt;IDbContextFactory&lt;/code&gt;, ensuring thread safety without lock contention.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Batch Inserts via EF Core (The Big Win)
&lt;/h3&gt;

&lt;p&gt;This was the most critical change. Instead of &lt;code&gt;context.Add(entity); context.SaveChanges();&lt;/code&gt; inside the loop, the new system accumulates entities in memory and flushes them in &lt;strong&gt;batches of 100+ rows&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Impact:&lt;/strong&gt; This reduces network round-trips by ~100x and drastically lowers transaction log overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Architecture &amp;amp; SOLID Principles
&lt;/h3&gt;

&lt;p&gt;To keep the code maintainable, I split the parsing logic into independent &lt;strong&gt;Processors&lt;/strong&gt;, each responsible for a specific part of the JSON (e.g., &lt;code&gt;ModelProcessor&lt;/code&gt;, &lt;code&gt;DiagramProcessor&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;SRP (Single Responsibility):&lt;/strong&gt; Each processor handles only its slice of the domain.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;DIP (Dependency Inversion):&lt;/strong&gt; High-level services depend on abstractions (&lt;code&gt;IEntityFactory&lt;/code&gt;, &lt;code&gt;IUnitOfWork&lt;/code&gt;), making the system easy to test and extend.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Reliability Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Retry Policies:&lt;/strong&gt; Up to 25 retries for transient DB errors (deadlocks, timeouts).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Graceful Degradation:&lt;/strong&gt; If one processor fails on bad data, it logs the error and continues, rather than crashing the entire import.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Optimized Parsing:&lt;/strong&gt; Switched to &lt;code&gt;JsonElement&lt;/code&gt; and &lt;code&gt;TryGetProperty&lt;/code&gt; for faster, low-allocation JSON traversal.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Results: 24x Faster
&lt;/h2&gt;

&lt;p&gt;The performance jump was massive, turning a "weekend task" into a "lunch break task".&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Original Version&lt;/th&gt;
&lt;th&gt;Optimized Version&lt;/th&gt;
&lt;th&gt;Improvement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total Time (40 GB)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~92 hours (3.8 days)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~3.8 hours&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~24x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Throughput&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8–12 rows/sec&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;192–300 rows/sec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~25x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time per 1000 rows&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;83–125 sec&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3–5 sec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~25x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parallelism&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1 thread&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8 workers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory Usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2 GB+&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~400 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~5x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fssaiy2wuwsghumsjm868.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fssaiy2wuwsghumsjm868.png" alt="Comparison of Import Performance: Original vs Optimized Architecture " width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Context Matters:&lt;/strong&gt; A 3-day script is fine once. It's fatal if you need to run it repeatedly. Always ask "how often will we run this?".&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Batching is King:&lt;/strong&gt; In EF Core, moving from single inserts to batching is often the single most effective performance upgrade you can make.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Parallelism Needs Limits:&lt;/strong&gt; Throwing 100 threads at SQL Server will just slow it down. Finding the "sweet spot" (e.g., 8 workers) is key.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Resilience is a Feature:&lt;/strong&gt; When running for hours, networks &lt;em&gt;will&lt;/em&gt; blink and deadlocks &lt;em&gt;will&lt;/em&gt; happen. Retry policies turn crashes into minor log warnings.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Future Plans
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Add comprehensive tests (xUnit + Moq) with 85%+ coverage for all processors.&lt;/li&gt;
&lt;li&gt;  Profile individual pipeline stages to find the next bottleneck (likely JSON parsing CPU time).&lt;/li&gt;
&lt;li&gt;  Expose configuration (batch size, thread count) to adapt to different server specs dynamically.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Check out the code:&lt;/strong&gt; &lt;a href="https://github.com/belochka1-04/ParsCsvSaveInDb" rel="noopener noreferrer"&gt;Link to GitHub Repository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>dotnet</category>
      <category>csharp</category>
      <category>sqlserver</category>
    </item>
    <item>
      <title>DevOps for 24 .NET Microservices: Prometheus Metrics + GitHub Actions CI/CD</title>
      <dc:creator>Татьяна Кузнецова</dc:creator>
      <pubDate>Mon, 29 Dec 2025 12:53:52 +0000</pubDate>
      <link>https://forem.com/belochka1-04/devops-for-24-net-microservices-prometheus-metrics-github-actions-cicd-l5j</link>
      <guid>https://forem.com/belochka1-04/devops-for-24-net-microservices-prometheus-metrics-github-actions-cicd-l5j</guid>
      <description>&lt;h1&gt;
  
  
  How I set up advanced Prometheus metrics for 24 .NET microservices, automated CI/CD via GitHub Actions. From health checks to full observability.
&lt;/h1&gt;

&lt;h2&gt;
  
  
  From health checks to full observability
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/__2131e9e/from-client-request-to-24-net-microservices-my-solo-migration-journey-7ah"&gt;previous article&lt;/a&gt;, I already set up &lt;strong&gt;health checks&lt;/strong&gt; in all 24 microservices, ready for Prometheus/Grafana.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But the client needed full metrics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;request counts by endpoints and status codes&lt;/li&gt;
&lt;li&gt;P95/P99 response times&lt;/li&gt;
&lt;li&gt;memory/CPU usage per service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;specific SQL metrics&lt;/strong&gt; for internal business processes&lt;/li&gt;
&lt;li&gt;automated CI/CD pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Advanced Prometheus metrics in .NET + SQL
&lt;/h2&gt;

&lt;p&gt;Added &lt;code&gt;prometheus-net.AspNetCore&lt;/code&gt; NuGet to &lt;strong&gt;each microservice&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;// Program.cs of each microservice (.NET 8)&lt;br&gt;
var builder = WebApplication.CreateBuilder(args);&lt;/p&gt;

&lt;p&gt;builder.Services.AddControllers();&lt;br&gt;
builder.Services.AddHealthChecks();&lt;br&gt;
builder.Services.AddHttpClientMetrics();&lt;/p&gt;

&lt;p&gt;var app = builder.Build();&lt;/p&gt;

&lt;p&gt;app.UseRouting();&lt;br&gt;
app.MapControllers();&lt;/p&gt;

&lt;p&gt;app.MapHealthChecks("/health");&lt;/p&gt;

&lt;p&gt;// Advanced HTTP metrics&lt;br&gt;
app.UseHttpMetrics();&lt;br&gt;
app.MapMetrics("/metrics"); // Prometheus scrapes here&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key metrics exposed on &lt;code&gt;/metrics&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
HTTP metrics (automatic)&lt;br&gt;
http_requests_total_total{method="GET", endpoint="/api/applications", status="200"}&lt;br&gt;
http_request_duration_seconds{quantile="0.95"}&lt;/p&gt;

&lt;p&gt;.NET runtime&lt;br&gt;
dotnet_total_memory_bytes&lt;/p&gt;

&lt;p&gt;SQL metrics (via sql_exporter)&lt;br&gt;
mssql_locks_wait_count_total&lt;br&gt;
mssql_query_stats_total_duration_ms&lt;br&gt;
mssql_database_size_bytes&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;sql_exporter&lt;/strong&gt; for specific MSSQL business metrics:&lt;br&gt;
prometheus.yml&lt;br&gt;
scrape_configs:&lt;/p&gt;

&lt;p&gt;job_name: 'mssql'&lt;br&gt;
static_configs:&lt;/p&gt;

&lt;p&gt;targets: ['sql_exporter:9187']&lt;br&gt;
metrics_path: /metrics&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: GitHub Actions CI/CD for 24 services
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Single pipeline&lt;/strong&gt; with matrix strategy:&lt;/p&gt;

&lt;p&gt;name: CI/CD Microservices&lt;br&gt;
on:&lt;br&gt;
push:&lt;br&gt;
branches: [ main ]&lt;br&gt;
pull_request:&lt;br&gt;
branches: [ main ]&lt;/p&gt;

&lt;p&gt;jobs:&lt;br&gt;
test:&lt;br&gt;
runs-on: ubuntu-latest&lt;br&gt;
steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uses: actions/checkout@v4&lt;/li&gt;
&lt;li&gt;name: Setup .NET 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'&lt;/li&gt;
&lt;li&gt;name: Test All Services
run: |
dotnet restore
dotnet test --no-restore --collect:"XPlat Code Coverage"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;build-push:&lt;br&gt;
needs: test&lt;br&gt;
runs-on: ubuntu-latest&lt;br&gt;
if: github.ref == 'refs/heads/main'&lt;br&gt;
strategy:&lt;br&gt;
matrix:&lt;br&gt;
service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ApplicationService&lt;/li&gt;
&lt;li&gt;JobService&lt;/li&gt;
&lt;li&gt;AuthService
# ... remaining 21 services
steps:&lt;/li&gt;
&lt;li&gt;uses: actions/checkout@v4&lt;/li&gt;
&lt;li&gt;name: Build &amp;amp; Push Docker
uses: docker/build-push-action@v5
with:
context: ./src/${{ matrix.service }}
push: true
tags: myregistry/${{ matrix.service }}:latest&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: Production stack (Windows services)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Prometheus + Grafana configured as Windows services&lt;/strong&gt; — auto-start on server reboot:&lt;/p&gt;

&lt;p&gt;✅ Prometheus (Windows Service) — scrapes /metrics from all 24 services + sql_exporter&lt;br&gt;
✅ Grafana (Windows Service) — dashboards with auto-login for client&lt;br&gt;
✅ Nginx (Windows Service) — reverse proxy&lt;br&gt;
✅ MSSQL + sql_exporter — SQL business metrics&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;prometheus.yml&lt;/strong&gt; (full scraping):&lt;br&gt;
scrape_configs:&lt;/p&gt;

&lt;p&gt;job_name: 'microservices'&lt;br&gt;
static_configs:&lt;/p&gt;

&lt;p&gt;targets:&lt;/p&gt;

&lt;p&gt;'application-service:80'&lt;/p&gt;

&lt;p&gt;'job-service:80'&lt;/p&gt;

&lt;p&gt;... all 24 services&lt;br&gt;
metrics_path: /metrics&lt;/p&gt;

&lt;p&gt;job_name: 'mssql'&lt;br&gt;
static_configs:&lt;/p&gt;

&lt;p&gt;targets: ['sql_exporter:9187']&lt;/p&gt;

&lt;h2&gt;
  
  
  Grafana dashboards (SQL business metrics)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SQL metrics visualization in Grafana:&lt;/strong&gt;&lt;br&gt;
📊 HTTP Overview&lt;br&gt;
├── Requests/sec by services&lt;br&gt;
├── P95 latency&lt;br&gt;
└── Error rates&lt;/p&gt;

&lt;p&gt;📊 SQL Business Metrics (sql_exporter)&lt;br&gt;
├── Database size (line chart)&lt;br&gt;
├── Lock wait times (gauge)&lt;br&gt;
├── Query duration TOP-10 (table)&lt;br&gt;
└── Business KPIs (pie charts):&lt;br&gt;
├─ Jobs completed today&lt;br&gt;
├─ Active sessions by status&lt;br&gt;
└─ Queue processing distribution&lt;/p&gt;

&lt;h2&gt;
  
  
  Evolution results
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;Health checks → Full metrics&lt;/strong&gt; — HTTP + .NET + SQL&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Automated CI/CD&lt;/strong&gt; — 24 services in 5 minutes&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Production services&lt;/strong&gt; — Prometheus/Grafana auto-start&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;SQL business metrics&lt;/strong&gt; — sql_exporter + pie charts in Grafana  &lt;/p&gt;

&lt;p&gt;Health Checks → HTTP Metrics → sql_exporter → Grafana Pie Charts&lt;br&gt;
↓&lt;br&gt;
GitHub Actions → Docker → Windows Services (Prometheus + Grafana)&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional DevOps experience
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;🎯 New skills:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sql_exporter&lt;/code&gt; for MSSQL business metrics&lt;/li&gt;
&lt;li&gt;Windows Services for Prometheus/Grafana&lt;/li&gt;
&lt;li&gt;Grafana pie charts for KPI dashboards&lt;/li&gt;
&lt;li&gt;GitHub Actions matrix (24 parallel builds)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;✅ Worked perfectly:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;app.UseHttpMetrics()&lt;/code&gt; — 2 lines of code&lt;/li&gt;
&lt;li&gt;sql_exporter — ready-made SQL metrics out of the box&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code in repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/belochka1-04/WebApi_microservices/tree/main/" rel="noopener noreferrer"&gt;https://github.com/belochka1-04/WebApi_microservices/tree/main/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📁 devops/&lt;br&gt;
├── prometheus.yml (24 services + sql_exporter)&lt;br&gt;
├── .github/workflows/ci-cd.yml&lt;br&gt;
├── docker-compose.prod.yml&lt;br&gt;
└── windows-services/ (Prometheus + Grafana setup)&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>devops</category>
      <category>githubactions</category>
      <category>docker</category>
    </item>
    <item>
      <title>From Client Request to 24 .NET Microservices: My Solo Migration Journey</title>
      <dc:creator>Татьяна Кузнецова</dc:creator>
      <pubDate>Mon, 29 Dec 2025 12:23:09 +0000</pubDate>
      <link>https://forem.com/belochka1-04/from-client-request-to-24-net-microservices-my-solo-migration-journey-7ah</link>
      <guid>https://forem.com/belochka1-04/from-client-request-to-24-net-microservices-my-solo-migration-journey-7ah</guid>
      <description>&lt;h2&gt;
  
  
  Backstory: the client's request
&lt;/h2&gt;

&lt;p&gt;It all started with a fairly typical client request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I have a MySQL database with tables and data in Excel. I want a robust mega-system that scales, runs without downtime, and grows with the business. Ready to build it step by step, but need the right architecture from the start."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At that point, the client had MySQL tables, Excel data, and a general vision of how everything should work, plus a strong desire to "build it properly." No specific requirements for development speed, load, or SLA—just the understanding that they needed scalable architecture that wouldn't require a complete rebuild in a year or two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 1: MVP with Web API + console apps
&lt;/h2&gt;

&lt;p&gt;First stage — quickly build a working prototype:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migrated data from MySQL and Excel to &lt;strong&gt;MSSQL&lt;/strong&gt; (client chose SQL Server as better suited for .NET ecosystem);
&lt;/li&gt;
&lt;li&gt;Built &lt;strong&gt;one Web API&lt;/strong&gt; on .NET with core CRUD operations;
&lt;/li&gt;
&lt;li&gt;Added several &lt;strong&gt;console applications&lt;/strong&gt; for background tasks (processing, import, reports).
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Initial architecture looked like this:&lt;/strong&gt;&lt;br&gt;
Console Apps (batch jobs)&lt;br&gt;
↓&lt;br&gt;
Web API (.NET + EF Core)&lt;br&gt;
↓&lt;br&gt;
MSSQL (data from MySQL + Excel)&lt;/p&gt;

&lt;p&gt;This approach delivered a working product to the client in 2-3 weeks. The system processed data, showed results, ran on the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 2: monolith pain points
&lt;/h2&gt;

&lt;p&gt;The client kept adding requirements and complicating logic, while I realized: continuing to pile everything into one service would just delay the inevitable rebuild. Classic monolith problems emerged:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every change = restart of the entire service&lt;/strong&gt;;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Impossible to scale selectively&lt;/strong&gt;;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downtime risk&lt;/strong&gt; — a bug in one area could crash the whole system;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance complexity&lt;/strong&gt; — all business logic in one place.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solution: gradual migration to microservices
&lt;/h2&gt;

&lt;p&gt;Once the final logic was shaped and it became clear how the end system should work, I decided to migrate to microservices architecture &lt;strong&gt;gradually&lt;/strong&gt;, keeping the system operational at every step. Principle: &lt;strong&gt;"strangler pattern"&lt;/strong&gt; — the monolith gradually gets "strangled," while functionality moves to new services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key migration principles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with &lt;strong&gt;business boundaries&lt;/strong&gt;;
&lt;/li&gt;
&lt;li&gt;Each new service must be &lt;strong&gt;fully autonomous&lt;/strong&gt; (Docker, tests, health checks);
&lt;/li&gt;
&lt;li&gt;Keep &lt;strong&gt;system operational&lt;/strong&gt; after each step.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Migration result: 24 microservices
&lt;/h2&gt;

&lt;p&gt;I single-handedly transformed the monolith into &lt;strong&gt;24 independent Web API microservices&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Project description&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Monolithic .NET application migration to microservices architecture: extracted 24 Web API microservices (Auth, Application, Job, Mask, User, etc.), each with its own bounded context and dedicated controllers. Inter-service communication implemented via RabbitMQ (event bus for async task-related events and entities) and HTTP (typed HttpClient + &lt;code&gt;IHttpClientFactory&lt;/code&gt;, service URLs and routes externalized to &lt;code&gt;appsettings.json&lt;/code&gt;).[file:1]  &lt;/p&gt;

&lt;p&gt;Data storage in MSSQL via EF Core, API access secured with JWT authentication, health checks, Swagger, and centralized logging (Serilog, NLog) configured.[file:1]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Full project code:&lt;/strong&gt; &lt;a href="https://github.com/belochka1-04/WebApi_microservices" rel="noopener noreferrer"&gt;https://github.com/belochka1-04/WebApi_microservices&lt;/a&gt; [file:1]&lt;/p&gt;

&lt;h2&gt;
  
  
  Status and completed tasks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What I've already done:&lt;/strong&gt;[file:1]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Extracted 24 Web API services with separate bounded contexts and contracts&lt;/li&gt;
&lt;li&gt;✅ Added unit/integration tests + Docker support to &lt;code&gt;ApplicationService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ Implemented health checks for all microservices (ready for Prometheus/Grafana)&lt;/li&gt;
&lt;li&gt;✅ Set up RabbitMQ and typed HttpClient communication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;In progress/planned:&lt;/strong&gt;[file:1]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔄 Expand test coverage to remaining services&lt;/li&gt;
&lt;li&gt;🔄 Standardize Docker containers across all 24 services&lt;/li&gt;
&lt;li&gt;🔄 Integrate Prometheus + Grafana for full observability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Complete project stack:&lt;/strong&gt;[file:1]&lt;/p&gt;

&lt;p&gt;Platform: .NET 8, ASP.NET Core Web API&lt;br&gt;
Language: C#&lt;br&gt;
Architecture: microservices (24 services), REST API, bounded contexts, SOLID, DI/IoC&lt;br&gt;
Data access: Entity Framework Core, SQL Server (MSSQL), migrations&lt;br&gt;
Communication: HTTP (typed HttpClient), RabbitMQ (event bus)&lt;br&gt;
Security: JWT authentication&lt;br&gt;
Logging: Serilog, NLog, health checks&lt;br&gt;
API docs: Swagger/OpenAPI&lt;br&gt;
Containerization: Docker (Dockerfile, docker-compose)&lt;br&gt;
Testing: unit/integration tests&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsd6b79cacrcpfxf2kmfm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsd6b79cacrcpfxf2kmfm.png" alt=" " width="418" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key migration lessons (from a solo developer)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What worked great:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gradual approach kept the system running&lt;/li&gt;
&lt;li&gt;Docker + health checks from day one saved tons of time
&lt;/li&gt;
&lt;li&gt;DTOs and explicit contracts prevented mass refactoring during DB changes&lt;/li&gt;
&lt;li&gt;RabbitMQ was perfect for async scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What was trickier:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Splitting shared MSSQL tables across services (ownership boundaries)&lt;/li&gt;
&lt;li&gt;Managing 24 Docker containers locally (docker-compose to the rescue)&lt;/li&gt;
&lt;li&gt;Realizing microservices = code + DevOps (monitoring, logs, deployment)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The system is now production-ready, but full observability awaits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus + Grafana&lt;/strong&gt; for health check and performance metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified docker-compose&lt;/strong&gt; for all 24 services + MSSQL + RabbitMQ
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD pipeline&lt;/strong&gt; for automated deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next article, I'll cover setting up this monitoring stack on Windows with Telegram alerts.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>microservices</category>
      <category>dotnet</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
