initial commit
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script>
|
||||
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Blazor Server vs WebAssembly: Choosing the Right Hosting Model | Indotalent Blog</title>
|
||||
<meta name="description" content="Compare Blazor Server and Blazor WebAssembly for enterprise applications. Which hosting model is right for your .NET 10 project? Complete analysis with pros and cons." />
|
||||
<meta name="keywords" content="Blazor Server vs WebAssembly, Blazor hosting model, Blazor Server SignalR, Blazor WASM performance" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/blazor-hosting-model.html" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Article",
|
||||
"headline": "Blazor Server vs WebAssembly: Choosing the Right Hosting Model",
|
||||
"description": "Compare Blazor Server and WASM for enterprise applications. Why Indotalent products use Blazor Server.",
|
||||
"author": { "@type": "Person", "name": "go2ismail" },
|
||||
"datePublished": "2026-06-01",
|
||||
"publisher": { "@type": "Organization", "name": "Indotalent" }
|
||||
}
|
||||
</script>
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://www.indotalent.com/" },
|
||||
{ "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://www.indotalent.com/blog/" },
|
||||
{ "@type": "ListItem", "position": 3, "name": "Blazor Server vs WebAssembly", "item": "https://www.indotalent.com/blog/blazor-hosting-model.html" }
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 20px -4px rgba(249,115,22,0.45)}article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}article h3{font-size:1.15rem;font-weight:600;margin:1.5rem 0 0.5rem}article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}article pre{background:#1f2937;color:#f9fafb;padding:16px;border-radius:10px;overflow-x:auto;margin:1rem 0;font-size:0.85rem;line-height:1.6}article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100"><div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5"><ol class="flex items-center gap-1.5 text-xs text-gray-400"><li><a href="../index.html" class="hover:text-brand-500">Home</a></li><li><span class="text-gray-300">/</span></li><li><a href="index.html" class="hover:text-brand-500">Blog</a></li><li><span class="text-gray-300">/</span></li><li><span class="text-gray-600 font-medium">Blazor Hosting Model</span></li></ol></div></nav>
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div></nav>
|
||||
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12"><div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3"><span class="text-xs font-semibold bg-blue-50 text-blue-700 border border-blue-200 rounded-full px-2.5 py-0.5">Blazor Server</span><span class="text-xs text-gray-400">June 2026 · 7 min read</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">Blazor Server vs WebAssembly: Choosing the Right Hosting Model</h1>
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-8"><p class="text-sm text-green-800 font-semibold mb-1">TL;DR</p><p class="text-sm text-green-700">For enterprise applications handling sensitive data, <strong>Blazor Server is the recommended choice</strong>. It keeps your code and data secure on the server, provides sub-second initial load times, and simplifies deployment. All Indotalent products use Blazor Server.</p></div>
|
||||
<article>
|
||||
<p>Blazor offers two primary hosting models, and choosing between them has significant implications for your application's architecture, performance, and security posture. Understanding the trade-offs is essential before starting any enterprise Blazor project.</p>
|
||||
<h2>Blazor Server: The Enterprise Choice</h2>
|
||||
<p>Blazor Server executes all C# code on the server. The browser receives a thin HTML document and maintains a persistent SignalR WebSocket connection. Every UI event — button clicks, form submissions, data grid interactions — is serialized over this connection, processed server-side, and the resulting UI diff is sent back. This architecture delivers sub-second initial load times, full access to server resources (database connections, file system, caching), and superior security because your source code, connection strings, and business logic never leave the server.</p>
|
||||
<h2>Blazor WebAssembly: Offline-First</h2>
|
||||
<p>Blazor WASM downloads the entire .NET runtime and your application DLLs to the browser. This enables fully offline operation and reduces server load, but the initial download can be 5-20 MB, leading to slow first-visit experiences. WASM applications must communicate with the backend exclusively through HTTP APIs, adding latency and complexity compared to Server's direct SignalR connection.</p>
|
||||
<h2>The Verdict for Enterprise Apps</h2>
|
||||
<p>For enterprise applications handling sensitive data — CRM systems with customer PII, HRM systems with employee records, WMS systems with inventory data — Blazor Server is the recommended choice. It keeps your intellectual property and sensitive data secure on the server, provides instant load times for internal users, and simplifies the deployment architecture to a single process. All Indotalent products use Blazor Server for exactly these reasons.</p>
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul><li>Blazor Server = sub-second loads, server-side security, simpler deployment</li><li>Blazor WASM = offline capability, reduced server load, larger initial download</li><li>Choose Server for enterprise apps with sensitive data</li><li>All 9 Indotalent products are built with Blazor Server</li></ul>
|
||||
</article>
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200 rounded-2xl"><p class="text-sm text-gray-700 font-semibold mb-2">See Blazor Server in action</p><p class="text-sm text-gray-600 mb-4">Every Indotalent product is a complete Blazor Server application. Complete source code — $21 each.</p><a href="../index.html#products" class="btn-primary text-sm">Explore Products</a></div>
|
||||
<div class="mt-6"><a href="index.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div></section>
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Blazor Server Performance Optimization: Real-World Techniques | Indotalent Blog</title>
|
||||
<meta name="description" content="ShouldRender, virtualization, no-tracking queries, and server-side pagination for Blazor Server apps. Production-tested techniques used in Indotalent products." />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/blazor-performance-optimization.html" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"Article","headline":"Blazor Server Performance Optimization: Real-World Techniques","author":{"@type":"Person","name":"go2ismail"},"datePublished":"2026-04-01","publisher":{"@type":"Organization","name":"Indotalent"}}</script>
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{ "@type":"ListItem","position":1,"name":"Home","item":"https://www.indotalent.com/" },{ "@type":"ListItem","position":2,"name":"Blog","item":"https://www.indotalent.com/blog/" },{ "@type":"ListItem","position":3,"name":"Blazor Performance Optimization","item":"https://www.indotalent.com/blog/blazor-performance-optimization.html" }]}</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script><script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}article h3{font-size:1.15rem;font-weight:600;margin:1.5rem 0 0.5rem}article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100"><div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5"><ol class="flex items-center gap-1.5 text-xs text-gray-400"><li><a href="../index.html" class="hover:text-brand-500">Home</a></li><li><span class="text-gray-300">/</span></li><li><a href="index.html" class="hover:text-brand-500">Blog</a></li><li><span class="text-gray-300">/</span></li><li><span class="text-gray-600 font-medium">Blazor Performance</span></li></ol></div></nav>
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div></nav>
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12"><div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3"><span class="text-xs font-semibold bg-blue-50 text-blue-700 border border-blue-200 rounded-full px-2.5 py-0.5">Blazor Server</span><span class="text-xs text-gray-400">April 2026 · 7 min read</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">Blazor Server Performance Optimization: Real-World Techniques</h1>
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-8"><p class="text-sm text-green-800 font-semibold mb-1">TL;DR</p><p class="text-sm text-green-700">Four techniques that cut rendering overhead by 50%+: ShouldRender overrides, MudBlazor virtualization, EF Core no-tracking queries, and server-side pagination. All Indotalent products ship with these baked in.</p></div>
|
||||
<article>
|
||||
<p>Blazor Server's SignalR-based architecture means every UI interaction involves a network round-trip. While this is fast on local networks or within the same datacenter, applications must be optimized for scenarios with higher latency or large datasets.</p>
|
||||
<h2>1. ShouldRender() Overrides</h2>
|
||||
<p>By default, Blazor re-renders a component when any of its parameters change. For components with complex rendering trees, override <code>ShouldRender()</code> to return false when no visual change is needed. This can cut rendering overhead by 50%+ in data-heavy pages.</p>
|
||||
<h2>2. Virtualization</h2>
|
||||
<p>MudBlazor's <code>Virtualize</code> component renders only the visible rows in a list. For grids with 10,000+ rows, this reduces DOM nodes from thousands to dozens, keeping the UI responsive even with massive datasets.</p>
|
||||
<h2>3. EF Core No-Tracking Queries</h2>
|
||||
<p>Read-only queries should use <code>.AsNoTracking()</code> to avoid EF Core's change tracking overhead. This can improve query performance by 30-50% for list views, dashboards, and reports.</p>
|
||||
<h2>4. Server-Side Pagination</h2>
|
||||
<p>Never load entire tables into memory. Implement pagination at the database level using <code>.Skip()</code> and <code>.Take()</code>. MudBlazor's data grid supports server-side pagination out of the box with its <code>ServerData</code> property.</p>
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul><li>Override ShouldRender() to cut rendering overhead by 50%+</li><li>Use MudBlazor virtualization for large datasets</li><li>Apply AsNoTracking() for all read-only EF Core queries</li><li>Implement server-side pagination — never load full tables</li></ul>
|
||||
</article>
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200 rounded-2xl"><p class="text-sm text-gray-700 font-semibold mb-2">See optimized Blazor code</p><p class="text-sm text-gray-600 mb-4">All Indotalent products ship with these optimizations. $21 each.</p><a href="../index.html#products" class="btn-primary text-sm">Explore Products</a></div>
|
||||
<div class="mt-6"><a href="index.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div></section>
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Securing Your Blazor Server Application with JWT and ASP.NET Core Identity | Indotalent Blog</title>
|
||||
<meta name="description" content="Four-layer security for Blazor Server: ASP.NET Core Identity, JWT authentication for REST APIs, policy-based authorization, and SignalR protection. Complete guide." />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/blazor-server-security-jwt.html" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"Article","headline":"Securing Your Blazor Server Application with JWT and ASP.NET Core Identity","author":{"@type":"Person","name":"go2ismail"},"datePublished":"2026-03-01","publisher":{"@type":"Organization","name":"Indotalent"}}</script>
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{ "@type":"ListItem","position":1,"name":"Home","item":"https://www.indotalent.com/" },{ "@type":"ListItem","position":2,"name":"Blog","item":"https://www.indotalent.com/blog/" },{ "@type":"ListItem","position":3,"name":"Blazor Server Security","item":"https://www.indotalent.com/blog/blazor-server-security-jwt.html" }]}</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script><script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}article h3{font-size:1.15rem;font-weight:600;margin:1.5rem 0 0.5rem}article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100"><div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5"><ol class="flex items-center gap-1.5 text-xs text-gray-400"><li><a href="../index.html" class="hover:text-brand-500">Home</a></li><li><span class="text-gray-300">/</span></li><li><a href="index.html" class="hover:text-brand-500">Blog</a></li><li><span class="text-gray-300">/</span></li><li><span class="text-gray-600 font-medium">Blazor Security</span></li></ol></div></nav>
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div></nav>
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12"><div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3"><span class="text-xs font-semibold bg-blue-50 text-blue-700 border border-blue-200 rounded-full px-2.5 py-0.5">Blazor Server</span><span class="text-xs text-gray-400">March 2026 · 8 min read</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">Securing Your Blazor Server Application with JWT and ASP.NET Core Identity</h1>
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-8"><p class="text-sm text-green-800 font-semibold mb-1">TL;DR</p><p class="text-sm text-green-700">Four-layer security: ASP.NET Core Identity for user management, JWT for API endpoints, policy-based authorization for fine-grained control, and SignalR protection for real-time channels.</p></div>
|
||||
<article>
|
||||
<p>Security is a layered concern in Blazor Server applications. The framework provides multiple protection mechanisms that must be configured correctly.</p>
|
||||
<h2>Layer 1: ASP.NET Core Identity</h2>
|
||||
<p>Identity provides user management, password hashing, role management, and cookie-based authentication. For Blazor Server, the default cookie authentication is sufficient for internal page access.</p>
|
||||
<h2>Layer 2: JWT for API Endpoints</h2>
|
||||
<p>When your Blazor app also exposes a REST API, cookies aren't appropriate. JWT bearer tokens provide stateless authentication. Indotalent products configure both authentication schemes simultaneously: cookies for Razor components, JWT for Minimal API endpoints.</p>
|
||||
<h2>Layer 3: Policy-Based Authorization</h2>
|
||||
<p>Role-based authorization works for simple cases, but policy-based authorization provides finer control. Define policies like "CanApproveOrders" or "CanViewFinancialReports" and apply them declaratively.</p>
|
||||
<h2>Layer 4: Secure SignalR</h2>
|
||||
<p>SignalR hubs handle real-time UI updates. Ensure hubs are protected with the <code>[Authorize]</code> attribute. Never transmit sensitive data through hub methods without encryption.</p>
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul><li>Identity + JWT dual authentication for Razor components and API endpoints</li><li>Policy-based authorization for fine-grained access control</li><li>SignalR hubs must be protected with [Authorize]</li><li>All Indotalent products come with pre-configured security layers</li></ul>
|
||||
</article>
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200 rounded-2xl"><p class="text-sm text-gray-700 font-semibold mb-2">Explore secure Blazor products</p><p class="text-sm text-gray-600 mb-4">All Indotalent products ship with JWT + Identity pre-configured. $21 each.</p><a href="../index.html#products" class="btn-primary text-sm">Explore Products</a></div>
|
||||
<div class="mt-6"><a href="index.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div></section>
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>From Clean Architecture to Vertical Slices: A Practical Migration Guide | Indotalent Blog</title>
|
||||
<meta name="description" content="Step-by-step approach to migrate from layered architecture to VSA, one feature at a time. Phase 1: pick one feature. Phase 2: extract validation. Phase 3: repeat." />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/clean-architecture-to-vsa-migration.html" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"Article","headline":"From Clean Architecture to Vertical Slices: A Practical Migration Guide","author":{"@type":"Person","name":"go2ismail"},"datePublished":"2026-03-01","publisher":{"@type":"Organization","name":"Indotalent"}}</script>
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{ "@type":"ListItem","position":1,"name":"Home","item":"https://www.indotalent.com/" },{ "@type":"ListItem","position":2,"name":"Blog","item":"https://www.indotalent.com/blog/" },{ "@type":"ListItem","position":3,"name":"Clean Architecture to VSA Migration","item":"https://www.indotalent.com/blog/clean-architecture-to-vsa-migration.html" }]}</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script><script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}article h3{font-size:1.15rem;font-weight:600;margin:1.5rem 0 0.5rem}article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100"><div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5"><ol class="flex items-center gap-1.5 text-xs text-gray-400"><li><a href="../index.html" class="hover:text-brand-500">Home</a></li><li><span class="text-gray-300">/</span></li><li><a href="index.html" class="hover:text-brand-500">Blog</a></li><li><span class="text-gray-300">/</span></li><li><span class="text-gray-600 font-medium">VSA Migration Guide</span></li></ol></div></nav>
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div></nav>
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12"><div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">March 2026 · 10 min read</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">From Clean Architecture to Vertical Slices: A Practical Migration Guide</h1>
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-8"><p class="text-sm text-green-800 font-semibold mb-1">TL;DR</p><p class="text-sm text-green-700">You don't need to rewrite your entire application. Migrate feature by feature. VSA preserves the good parts of Clean Architecture while reorganizing by business capability.</p></div>
|
||||
<article>
|
||||
<p>You don't need to rewrite your entire application to adopt VSA. The migration can be gradual, feature by feature. Here's the step-by-step approach I've used on production systems.</p>
|
||||
<h2>Phase 1: Pick One Feature</h2>
|
||||
<p>Choose a self-contained business operation — creating an order, registering a customer, processing a payment. Create a new folder: <code>Features/[FeatureName]/</code>. Inside, create a single file containing: the Minimal API endpoint mapping, the MediatR command or query record, and the handler class. Move the existing controller action logic and service layer code into the handler.</p>
|
||||
<h2>Phase 2: Extract Validation</h2>
|
||||
<p>If you're using FluentValidation, add a validator class in the same folder. If not, this is the perfect time to introduce it. Register validators via assembly scanning so they're discovered automatically.</p>
|
||||
<h2>Phase 3: Repeat</h2>
|
||||
<p>Continue migrating features one at a time. You'll notice that as more features move to VSA, the old layered structure becomes thinner. Eventually, you can delete entire projects that are now empty.</p>
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul><li>Migration is additive, not destructive — VSA features run alongside layered code</li><li>Start with one self-contained business operation</li><li>Add validation in the same feature folder using FluentValidation</li><li>Gradually delete empty projects as more features migrate</li></ul>
|
||||
</article>
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200 rounded-2xl"><p class="text-sm text-gray-700 font-semibold mb-2">Get VSA-ready source code</p><p class="text-sm text-gray-600 mb-4">Every Indotalent product demonstrates a complete VSA migration target. $21 each.</p><a href="../index.html#products" class="btn-primary text-sm">Explore Products</a></div>
|
||||
<div class="mt-6"><a href="index.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div></section>
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,488 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script>
|
||||
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>CQRS + MediatR in VSA: Make Your Code Not Spaghetti Code | Indotalent Blog</title>
|
||||
<meta name="description" content="How CQRS and MediatR inside Vertical Slice Architecture eliminate spaghetti code. See the before-and-after transformation of a tangled controller into clean VSA slices." />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/cqrs-mediatr-no-spaghetti-code.html" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"Article","headline":"CQRS + MediatR in VSA: Make Your Code Not Spaghetti Code","author":{"@type":"Person","name":"go2ismail"},"datePublished":"2026-07-01","publisher":{"@type":"Organization","name":"Indotalent"}}</script>
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{ "@type":"ListItem","position":1,"name":"Home","item":"https://www.indotalent.com/" },{ "@type":"ListItem","position":2,"name":"Blog","item":"https://www.indotalent.com/blog/" },{ "@type":"ListItem","position":3,"name":"CQRS + MediatR: No Spaghetti Code","item":"https://www.indotalent.com/blog/cqrs-mediatr-no-spaghetti-code.html" }]}</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 20px -4px rgba(249,115,22,0.45)}article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}article h3{font-size:1.15rem;font-weight:600;margin:1.5rem 0 0.5rem}article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}article pre{background:#1f2937;color:#f9fafb;padding:16px;border-radius:10px;overflow-x:auto;margin:1rem 0;font-size:0.85rem;line-height:1.6}article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}article .spaghetti-box{background:#fef2f2;border:1px solid #fecaca;border-left:5px solid #ef4444;padding:14px 18px;border-radius:0 10px 10px 0;margin:1.2rem 0}article .clean-box{background:#f0fdf4;border:1px solid #bbf7d0;border-left:5px solid #22c55e;padding:14px 18px;border-radius:0 10px 10px 0;margin:1.2rem 0}article .label-spaghetti{display:inline-block;background:#fee2e2;color:#dc2626;font-weight:800;font-size:0.75rem;padding:3px 12px;border-radius:6px;border:1px solid #fecaca;margin-bottom:0.5rem;letter-spacing:0.5px}article .label-clean{display:inline-block;background:#dcfce7;color:#16a34a;font-weight:800;font-size:0.75rem;padding:3px 12px;border-radius:6px;border:1px solid #bbf7d0;margin-bottom:0.5rem;letter-spacing:0.5px}article .vsa-glow{background:linear-gradient(135deg,rgba(249,115,22,0.06),rgba(234,88,12,0.02));border:1px solid rgba(249,115,22,0.15);border-radius:12px;padding:16px 20px;margin:1.2rem 0}</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100"><div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5"><ol class="flex items-center gap-1.5 text-xs text-gray-400"><li><a href="../index.html" class="hover:text-brand-500">Home</a></li><li><span class="text-gray-300">/</span></li><li><a href="index.html" class="hover:text-brand-500">Blog</a></li><li><span class="text-gray-300">/</span></li><li><span class="text-gray-600 font-medium">CQRS + MediatR: No Spaghetti Code</span></li></ol></div></nav>
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div></nav>
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12"><div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3"><span class="text-xs font-semibold bg-green-50 text-green-700 border border-green-200 rounded-full px-2.5 py-0.5">CQRS</span><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs font-semibold bg-purple-50 text-purple-700 border border-purple-200 rounded-full px-2.5 py-0.5">MediatR</span><span class="text-xs text-gray-400">July 2026 · 14 min read</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">CQRS + MediatR in VSA: Make Your Code Not Spaghetti Code</h1>
|
||||
<div class="bg-red-50 border border-red-200 rounded-xl p-4 mb-8"><p class="text-sm text-red-800 font-semibold mb-1">🍝 Spaghetti Code Alert</p><p class="text-sm text-red-700">We're exposing real spaghetti code — the kind that lives inside traditional layered architecture controllers. Then we'll show you how CQRS + MediatR, powered by Vertical Slice Architecture, eliminates it completely.</p></div>
|
||||
<article>
|
||||
|
||||
<p>Let's call it what it is: spaghetti code. Every .NET developer has seen it. Every developer has probably written it at 11 PM on a Friday, promising to refactor later. That controller with 700 lines of business logic, validation, database access, email sending, and who-knows-what-else — all tangled together into one unmaintainable mess. That is spaghetti code. And in a traditional layered architecture, spaghetti code is not the exception. It is the inevitable destination.</p>
|
||||
|
||||
<p>Why does layered architecture breed spaghetti code? Because the architecture itself scatters related logic across multiple projects and layers. An order creation feature touches 7 files across 4 projects: <code>OrderController.cs</code>, <code>IOrderService.cs</code>, <code>OrderService.cs</code>, <code>IOrderRepository.cs</code>, <code>OrderRepository.cs</code>, <code>Order.cs</code>, <code>CreateOrderDto.cs</code>. To understand how an order is created, you must mentally stitch together code from all these files — like following individual strands of spaghetti across the entire plate. The architecture itself creates the tangle. Spaghetti code isn't a developer problem in layered architecture; it's an architectural problem.</p>
|
||||
|
||||
<p>But there is a way out. CQRS (Command Query Responsibility Segregation) combined with MediatR, inside a Vertical Slice Architecture (VSA), eliminates spaghetti code at the architectural level. Instead of organizing code by technical layer — all controllers together, all services together, all repositories together — VSA organizes code by business feature. Each feature slice contains everything that feature needs: the command, the handler, the validator, and any domain logic. One folder. One feature. Zero spaghetti code.</p>
|
||||
|
||||
<h2>The Problem: How Layered Architecture Creates Spaghetti Code</h2>
|
||||
|
||||
<p>Before we dive into the VSA solution, let's understand exactly how layered architecture produces spaghetti code. The fundamental issue is that layered architecture groups code by <em>what things are</em> technically — controllers, services, repositories — rather than <em>what they do</em> for the business. This means that any single business operation is spread across multiple layers and multiple projects. When you need to trace the flow of "creating an order," you jump from the presentation layer to the application layer to the domain layer to the infrastructure layer. Each jump is an opportunity for spaghetti code to creep in.</p>
|
||||
|
||||
<p>Adding a new feature in layered architecture means touching files in every layer. Adding validation? That goes in the controller, or maybe the service, or maybe a FluentValidation validator injected into the service — the decision is arbitrary, and that arbitrariness is what spawns spaghetti code. Need to send an email after order creation? Do you put it in the service? The controller? A domain event? There's no clear answer in layered architecture, so developers just stuff it wherever it fits, and the spaghetti code grows thicker with every commit.</p>
|
||||
|
||||
<h2>Before: Spaghetti Code in a Layered Architecture</h2>
|
||||
|
||||
<p>Here's what spaghetti code looks like in a typical layered .NET application. This is a single <code>OrdersController</code> that handles creating an order. Notice how validation, data access, discount calculation, tax logic, email sending, and logging are all mashed together. This is textbook spaghetti code — every concern entangled with every other concern.</p>
|
||||
|
||||
<div class="spaghetti-box">
|
||||
<span class="label-spaghetti">🍝 SPAGHETTI CODE — Layered Architecture (BEFORE)</span>
|
||||
<pre style="margin:0.5rem 0 0 0;background:#1f2937">// Controllers/OrdersController.cs
|
||||
// Spaghetti code: 7 concerns in 1 method across 4+ projects
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class OrdersController : ControllerBase
|
||||
{
|
||||
private readonly AppDbContext _context;
|
||||
private readonly IEmailService _emailService;
|
||||
private readonly ILogger<OrdersController> _logger;
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create(CreateOrderDto dto)
|
||||
{
|
||||
// Spaghetti concern #1: Validation mixed inline
|
||||
if (string.IsNullOrWhiteSpace(dto.CustomerEmail))
|
||||
return BadRequest("Email required");
|
||||
if (dto.Items == null || dto.Items.Count == 0)
|
||||
return BadRequest("No items");
|
||||
|
||||
// Spaghetti concern #2: Direct data access in controller
|
||||
var customer = await _context.Customers
|
||||
.FirstOrDefaultAsync(c => c.Email == dto.CustomerEmail);
|
||||
if (customer == null) return BadRequest("Customer not found");
|
||||
|
||||
decimal subtotal = 0;
|
||||
var orderItems = new List<OrderItem>();
|
||||
|
||||
foreach (var item in dto.Items)
|
||||
{
|
||||
var product = await _context.Products.FindAsync(item.ProductId);
|
||||
if (product == null)
|
||||
return BadRequest($"Product {item.ProductId} not found");
|
||||
|
||||
// Spaghetti concern #3: Business rules tangled with DB
|
||||
if (product.Stock < item.Quantity)
|
||||
return BadRequest($"Out of stock: {product.Name}");
|
||||
|
||||
// Spaghetti concern #4: Discount logic hardcoded
|
||||
decimal discount = 0;
|
||||
if (customer.IsPremium && product.Category == "Electronics")
|
||||
discount = product.Price * 0.10m;
|
||||
else if (customer.IsPremium)
|
||||
discount = product.Price * 0.05m;
|
||||
else if (item.Quantity > 10)
|
||||
discount = product.Price * 0.02m;
|
||||
|
||||
var lineTotal = (product.Price - discount) * item.Quantity;
|
||||
subtotal += lineTotal;
|
||||
|
||||
orderItems.Add(new OrderItem
|
||||
{
|
||||
ProductId = product.Id,
|
||||
Quantity = item.Quantity,
|
||||
UnitPrice = product.Price,
|
||||
Discount = discount,
|
||||
LineTotal = lineTotal
|
||||
});
|
||||
|
||||
product.Stock -= item.Quantity;
|
||||
}
|
||||
|
||||
// Spaghetti concern #5: Tax logic mixed in
|
||||
decimal taxRate = customer.Country switch
|
||||
{
|
||||
"US" => 0.07m,
|
||||
"UK" => 0.20m,
|
||||
"AU" => 0.10m,
|
||||
_ => 0.10m
|
||||
};
|
||||
decimal tax = subtotal * taxRate;
|
||||
decimal total = subtotal + tax;
|
||||
|
||||
var order = new Order
|
||||
{
|
||||
CustomerId = customer.Id,
|
||||
Subtotal = subtotal,
|
||||
Tax = tax,
|
||||
Total = total,
|
||||
Status = "Pending",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
Items = orderItems
|
||||
};
|
||||
|
||||
_context.Orders.Add(order);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Spaghetti concern #6: Email sending inside controller
|
||||
await _emailService.SendAsync(customer.Email,
|
||||
"Order Confirmed",
|
||||
$"Dear {customer.Name}, order #{order.Id} total: {total:C}");
|
||||
|
||||
// Spaghetti concern #7: Logging embedded
|
||||
_logger.LogInformation("Order {Id} created for {Email}",
|
||||
order.Id, customer.Email);
|
||||
|
||||
return Ok(new { order.Id, order.Total });
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<p>Look at this spaghetti code carefully. Seven distinct concerns — validation, data access, business rules, discount calculation, tax calculation, email notification, and logging — are all entangled in a single method. This is what layered architecture produces over time. Each new requirement adds another strand of spaghetti to the plate. Need to support a new country's tax rate? You're editing the same method that also handles email sending. Want to change how premium discounts work? You're touching code that also validates email addresses. The spaghetti code entanglement means you cannot change one thing without risking everything else.</p>
|
||||
|
||||
<p>And testing this spaghetti code? Forget about it. The discount logic is buried inside a loop that's inside a method that directly instantiates <code>AppDbContext</code>. You can't unit-test the discount calculation without spinning up a database. You can't test the tax logic without mocking an email service — which has nothing to do with tax. Spaghetti code doesn't just look bad. It actively prevents you from writing tests, which means you can't refactor, which means the spaghetti code gets worse. It's a death spiral.</p>
|
||||
|
||||
<h2>The Solution: VSA + CQRS + MediatR = No Spaghetti Code</h2>
|
||||
|
||||
<p>Vertical Slice Architecture flips the script entirely. Instead of organizing code by technical layer, VSA organizes by business feature. Every feature lives in its own folder. Inside that folder, you'll find the command/query, the handler, the validator, and any domain logic — all in one place. CQRS, implemented through MediatR, provides the messaging backbone. Commands (writes) and Queries (reads) are clearly separated. Cross-cutting concerns like validation and logging are handled by MediatR pipeline behaviors — written once, applied everywhere.</p>
|
||||
|
||||
<p>In VSA, the folder structure for our order creation feature looks like this:</p>
|
||||
|
||||
<pre style="background:#1f2937;color:#f9fafb;padding:16px;border-radius:10px;overflow-x:auto;margin:1rem 0;font-size:0.85rem;line-height:1.6">Features/
|
||||
└── Orders/
|
||||
├── CreateOrder/
|
||||
│ ├── CreateOrderCommand.cs ← The command (DTO)
|
||||
│ ├── CreateOrderHandler.cs ← Business logic only
|
||||
│ ├── CreateOrderValidator.cs ← Validation rules
|
||||
│ └── CreateOrderEndpoint.cs ← Minimal API endpoint
|
||||
├── GetOrderById/
|
||||
│ ├── GetOrderByIdQuery.cs
|
||||
│ └── GetOrderByIdHandler.cs
|
||||
└── Shared/
|
||||
├── Order.cs ← Domain entity
|
||||
└── OrderItem.cs</pre>
|
||||
|
||||
<p>One feature, one folder. No jumping between projects. No hunting through layers. No spaghetti code. When you need to understand how an order is created, you open the <code>CreateOrder</code> folder and everything is right there. When you need to add a new field to orders, you change files in one folder — not across four projects. This is the architectural antidote to spaghetti code.</p>
|
||||
|
||||
<h2>After: Clean Code with VSA, CQRS, and MediatR</h2>
|
||||
|
||||
<p>Now let's see the same order creation feature rebuilt with CQRS + MediatR inside a Vertical Slice Architecture. Every concern is separated. Every class has a single responsibility. There is not a trace of spaghetti code anywhere.</p>
|
||||
|
||||
<div class="clean-box">
|
||||
<span class="label-clean">✅ CLEAN CODE — VSA + CQRS + MediatR (AFTER)</span>
|
||||
</div>
|
||||
|
||||
<h3>1. The Command — A Simple, Focused DTO</h3>
|
||||
<p>The command is a plain record. It carries data. Nothing else. This is not spaghetti code; it's a single-purpose data structure.</p>
|
||||
|
||||
<div class="clean-box">
|
||||
<span class="label-clean">✅ Features/Orders/CreateOrder/CreateOrderCommand.cs</span>
|
||||
<pre style="margin:0.5rem 0 0 0;background:#1f2937">public record CreateOrderCommand(
|
||||
string CustomerEmail,
|
||||
List<OrderItemDto> Items
|
||||
) : IRequest<CreateOrderResult>;
|
||||
|
||||
public record OrderItemDto(Guid ProductId, int Quantity);
|
||||
public record CreateOrderResult(Guid OrderId, decimal Total);</pre>
|
||||
</div>
|
||||
|
||||
<h3>2. The Validator — Validation Lives in Its Own Class</h3>
|
||||
<p>Validation is extracted into a dedicated FluentValidation validator. No validation logic pollutes the handler or the controller. This is how you prevent spaghetti code: one class, one job.</p>
|
||||
|
||||
<div class="clean-box">
|
||||
<span class="label-clean">✅ Features/Orders/CreateOrder/CreateOrderValidator.cs</span>
|
||||
<pre style="margin:0.5rem 0 0 0;background:#1f2937">public class CreateOrderValidator
|
||||
: AbstractValidator<CreateOrderCommand>
|
||||
{
|
||||
public CreateOrderValidator()
|
||||
{
|
||||
RuleFor(x => x.CustomerEmail)
|
||||
.NotEmpty()
|
||||
.EmailAddress();
|
||||
RuleFor(x => x.Items)
|
||||
.NotEmpty()
|
||||
.Must(items => items.Count > 0);
|
||||
RuleForEach(x => x.Items).ChildRules(item =>
|
||||
{
|
||||
item.RuleFor(i => i.ProductId)
|
||||
.NotEqual(Guid.Empty);
|
||||
item.RuleFor(i => i.Quantity)
|
||||
.InclusiveBetween(1, 1000);
|
||||
});
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<h3>3. The Handler — Business Logic Only, Zero Spaghetti Code</h3>
|
||||
<p>The handler is the heart of the feature slice. It contains <strong>only</strong> business logic. No validation (that's already done by the pipeline). No logging (also in the pipeline). No email sending (that's a domain event). Just pure, focused business logic — the polar opposite of spaghetti code.</p>
|
||||
|
||||
<div class="clean-box">
|
||||
<span class="label-clean">✅ Features/Orders/CreateOrder/CreateOrderHandler.cs</span>
|
||||
<pre style="margin:0.5rem 0 0 0;background:#1f2937">public class CreateOrderHandler
|
||||
: IRequestHandler<CreateOrderCommand, CreateOrderResult>
|
||||
{
|
||||
private readonly AppDbContext _context;
|
||||
|
||||
public CreateOrderHandler(AppDbContext context)
|
||||
=> _context = context;
|
||||
|
||||
public async Task<CreateOrderResult> Handle(
|
||||
CreateOrderCommand cmd, CancellationToken ct)
|
||||
{
|
||||
var customer = await _context.Customers
|
||||
.Where(c => c.Email == cmd.CustomerEmail)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
|
||||
if (customer is null)
|
||||
throw new NotFoundException("Customer not found");
|
||||
|
||||
var order = Order.Create(customer.Id);
|
||||
|
||||
foreach (var item in cmd.Items)
|
||||
{
|
||||
var product = await _context.Products
|
||||
.FindAsync([item.ProductId], ct);
|
||||
|
||||
if (product is null)
|
||||
throw new NotFoundException(
|
||||
$"Product {item.ProductId}");
|
||||
|
||||
product.ReserveStock(item.Quantity);
|
||||
|
||||
var discount = customer.CalculateDiscount(
|
||||
product, item.Quantity);
|
||||
|
||||
order.AddItem(product, item.Quantity, discount);
|
||||
}
|
||||
|
||||
order.CalculateTotals(customer.Country);
|
||||
|
||||
_context.Orders.Add(order);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
|
||||
order.AddDomainEvent(
|
||||
new OrderCreatedDomainEvent(order.Id, customer.Email));
|
||||
|
||||
return new CreateOrderResult(order.Id, order.Total);
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<h3>4. Domain Logic — Rich, Testable, No Spaghetti Code</h3>
|
||||
<p>Business rules live in the domain entities themselves — where they belong. The discount calculation and stock reservation are methods on the entity, not buried in a controller. This is how you eliminate spaghetti code: put logic where it's discoverable.</p>
|
||||
|
||||
<div class="clean-box">
|
||||
<span class="label-clean">✅ Features/Orders/Shared/Order.cs (Domain Entity)</span>
|
||||
<pre style="margin:0.5rem 0 0 0;background:#1f2937">public class Order
|
||||
{
|
||||
public Guid Id { get; private set; }
|
||||
public Guid CustomerId { get; private set; }
|
||||
public decimal Subtotal { get; private set; }
|
||||
public decimal Tax { get; private set; }
|
||||
public decimal Total { get; private set; }
|
||||
public OrderStatus Status { get; private set; }
|
||||
public List<OrderItem> Items { get; private set; } = [];
|
||||
|
||||
public static Order Create(Guid customerId)
|
||||
=> new() { Id = Guid.NewGuid(), CustomerId = customerId,
|
||||
Status = OrderStatus.Pending };
|
||||
|
||||
public void AddItem(Product product, int quantity, decimal discount)
|
||||
{
|
||||
var lineTotal = (product.Price - discount) * quantity;
|
||||
Items.Add(new OrderItem
|
||||
{
|
||||
ProductId = product.Id,
|
||||
Quantity = quantity,
|
||||
UnitPrice = product.Price,
|
||||
Discount = discount,
|
||||
LineTotal = lineTotal
|
||||
});
|
||||
Subtotal += lineTotal;
|
||||
}
|
||||
|
||||
public void CalculateTotals(string country)
|
||||
{
|
||||
Tax = Subtotal * TaxRate.For(country);
|
||||
Total = Subtotal + Tax;
|
||||
}
|
||||
|
||||
// Rich domain behavior — no spaghetti code here
|
||||
public void MarkAsShipped() =>
|
||||
Status = OrderStatus.Shipped;
|
||||
public void Cancel() =>
|
||||
Status = OrderStatus.Cancelled;
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<h3>5. Pipeline Behaviors — Cross-Cutting Concerns, Applied Once</h3>
|
||||
<p>MediatR pipeline behaviors are the secret weapon against spaghetti code. Instead of copying validation and logging code into every handler (which is how spaghetti code starts), you write it once as a behavior and it applies to every handler automatically. This is the DRY principle at the architectural level.</p>
|
||||
|
||||
<div class="clean-box">
|
||||
<span class="label-clean">✅ Pipeline/ValidationBehavior.cs — applied to ALL handlers</span>
|
||||
<pre style="margin:0.5rem 0 0 0;background:#1f2937">public class ValidationBehavior<TRequest, TResponse>
|
||||
: IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : IRequest<TResponse>
|
||||
{
|
||||
private readonly IServiceProvider _sp;
|
||||
|
||||
public async Task<TResponse> Handle(TRequest request,
|
||||
RequestHandlerDelegate<TResponse> next,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var validator = _sp.GetService<
|
||||
IValidator<TRequest>>();
|
||||
|
||||
if (validator is not null)
|
||||
{
|
||||
var result = await validator
|
||||
.ValidateAsync(request, ct);
|
||||
if (!result.IsValid)
|
||||
throw new ValidationException(
|
||||
result.Errors);
|
||||
}
|
||||
|
||||
return await next();
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<div class="clean-box">
|
||||
<span class="label-clean">✅ Pipeline/LoggingBehavior.cs — applied to ALL handlers</span>
|
||||
<pre style="margin:0.5rem 0 0 0;background:#1f2937">public class LoggingBehavior<TRequest, TResponse>
|
||||
: IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : IRequest<TResponse>
|
||||
{
|
||||
private readonly ILogger<LoggingBehavior
|
||||
<TRequest, TResponse>> _logger;
|
||||
|
||||
public async Task<TResponse> Handle(TRequest request,
|
||||
RequestHandlerDelegate<TResponse> next,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var name = typeof(TRequest).Name;
|
||||
_logger.LogInformation("➡️ {Name} started", name);
|
||||
var sw = Stopwatch.StartNew();
|
||||
var response = await next();
|
||||
sw.Stop();
|
||||
_logger.LogInformation(
|
||||
"✅ {Name} completed in {Ms}ms", name, sw.ElapsedMilliseconds);
|
||||
return response;
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<h3>6. Domain Events — Side Effects, Not Spaghetti Code</h3>
|
||||
<p>Email sending after order creation is a side effect — it doesn't belong in the handler. With domain events, the handler raises an event, and a separate handler sends the email. No spaghetti code entanglement between order creation and email sending.</p>
|
||||
|
||||
<div class="clean-box">
|
||||
<span class="label-clean">✅ Features/Orders/CreateOrder/OrderCreatedDomainEventHandler.cs</span>
|
||||
<pre style="margin:0.5rem 0 0 0;background:#1f2937">public class OrderCreatedDomainEventHandler
|
||||
: INotificationHandler<OrderCreatedDomainEvent>
|
||||
{
|
||||
private readonly IEmailService _email;
|
||||
private readonly AppDbContext _context;
|
||||
|
||||
public async Task Handle(
|
||||
OrderCreatedDomainEvent evt, CancellationToken ct)
|
||||
{
|
||||
var customer = await _context.Customers
|
||||
.FindAsync([evt.CustomerId], ct);
|
||||
|
||||
await _email.SendAsync(customer!.Email,
|
||||
"Order Confirmed ✅",
|
||||
$"Your order #{evt.OrderId} is being processed.");
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<h3>7. The Endpoint — Thin, Clean, No Spaghetti Code</h3>
|
||||
<p>In VSA, the endpoint (whether a controller action or Minimal API) is razor-thin. It does exactly one thing: send the command through MediatR. That's it. This is what "no spaghetti code" looks like at the API surface.</p>
|
||||
|
||||
<div class="clean-box">
|
||||
<span class="label-clean">✅ Features/Orders/CreateOrder/CreateOrderEndpoint.cs</span>
|
||||
<pre style="margin:0.5rem 0 0 0;background:#1f2937">public static class CreateOrderEndpoint
|
||||
{
|
||||
public static void Map(IEndpointRouteBuilder app)
|
||||
{
|
||||
app.MapPost("/api/orders", async (
|
||||
CreateOrderCommand cmd,
|
||||
IMediator mediator,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var result = await mediator.Send(cmd, ct);
|
||||
return Results.Created(
|
||||
$"/api/orders/{result.OrderId}", result);
|
||||
})
|
||||
.WithName("CreateOrder")
|
||||
.WithTags("Orders");
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<h2>The Spaghetti Code Comparison: Side by Side</h2>
|
||||
|
||||
<p>Let's put it side by side so the difference is undeniable. On the left: the spaghetti code layered approach. On the right: the clean VSA + CQRS + MediatR approach.</p>
|
||||
|
||||
<div class="vsa-glow">
|
||||
<table style="width:100%;font-size:0.85rem;border-collapse:collapse">
|
||||
<thead>
|
||||
<tr style="background:#fef2f2"><th style="padding:10px;text-align:left;color:#dc2626;width:50%;border:1px solid #fecaca">🍝 Layered Spaghetti Code</th><th style="padding:10px;text-align:left;color:#16a34a;width:50%;border:1px solid #bbf7d0;background:#f0fdf4">✅ VSA + CQRS + MediatR</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">7 files across 4+ projects per feature</td><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">3-4 files in 1 folder per feature</td></tr>
|
||||
<tr><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">Validation scattered across controllers and services</td><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">Single FluentValidation validator per command</td></tr>
|
||||
<tr><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">Business logic mixed with data access</td><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">Handler = business logic only</td></tr>
|
||||
<tr><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">Logging copied into every method</td><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">One LoggingBehavior for all handlers</td></tr>
|
||||
<tr><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">Email sending embedded in controller</td><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">Decoupled domain event handler</td></tr>
|
||||
<tr><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">Impossible to unit test individual concerns</td><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">Every class is independently testable</td></tr>
|
||||
<tr><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">One change touches unrelated code</td><td style="padding:10px;border:1px solid #e5e7eb;vertical-align:top">Changes stay within the feature folder</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2>Why CQRS + MediatR + VSA Is the Spaghetti Code Antidote</h2>
|
||||
|
||||
<p>The combination of these three patterns doesn't just reduce spaghetti code — it makes it architecturally impossible. Here's why each piece matters:</p>
|
||||
|
||||
<p><strong>Vertical Slice Architecture</strong> groups code by feature, not by technical layer. Since all the code for "create an order" lives in one folder, there's no temptation to scatter logic across projects. The architecture itself enforces cohesion. You cannot create spaghetti code when everything for a feature is co-located.</p>
|
||||
|
||||
<p><strong>CQRS</strong> forces you to separate reads from writes. A command handler only handles commands. A query handler only handles queries. You can't accidentally mix read logic into a write operation because the interfaces are different: <code>IRequest<TResponse></code> for commands, <code>IRequest<TResult></code> for queries. The type system itself prevents spaghetti code.</p>
|
||||
|
||||
<p><strong>MediatR</strong> provides the pipeline that handles cross-cutting concerns. Validation, logging, transaction management, authorization — these are all behaviors that execute before and after your handler. Your handler doesn't know about them. They don't know about your handler. This is the separation of concerns that spaghetti code violates, enforced by the framework.</p>
|
||||
|
||||
<div class="vsa-glow">
|
||||
<p style="margin-bottom:0.5rem;font-weight:700;color:#c2410c">🔥 Indotalent Insight</p>
|
||||
<p style="margin-bottom:0">Every Indotalent product — from the CRM to the WMS, from the HRM to the CMS — is built with exactly this architecture: VSA + CQRS + MediatR. Not a single line of spaghetti code. You get the complete source code for $21, and you can see for yourself how a production-grade .NET 10 application looks when it's clean from the ground up.</p>
|
||||
</div>
|
||||
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul>
|
||||
<li><strong>Layered architecture breeds spaghetti code</strong> by scattering related logic across multiple projects and layers — 7 files in 4 projects for one feature.</li>
|
||||
<li><strong>VSA eliminates spaghetti code</strong> by grouping all code for a feature in a single folder — 3-4 files, one place, full cohesion.</li>
|
||||
<li><strong>CQRS prevents concerns from mixing</strong> — commands and queries are different types, so you can't accidentally blend read and write logic.</li>
|
||||
<li><strong>MediatR pipeline behaviors</strong> handle cross-cutting concerns once, stopping the copy-paste spaghetti code cycle dead in its tracks.</li>
|
||||
<li><strong>Domain events decouple side effects</strong> — email sending, audit logging, and cache invalidation live in their own handlers, not tangled into business logic.</li>
|
||||
<li><strong>The result is fully testable code</strong> — every class has one responsibility, making unit testing trivial compared to the spaghetti code alternative.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Stop Writing Spaghetti Code. Start with VSA.</h2>
|
||||
|
||||
<p>Spaghetti code isn't a personality trait. It's not something you're doomed to write because you're a "messy" developer. Spaghetti code is a symptom of the wrong architecture. When your architecture scatters related code across layers and projects, it rewards tangling. When your architecture groups code by feature, it rewards cleanliness. VSA + CQRS + MediatR is that clean architecture — and once you try it, you'll wonder how you ever tolerated spaghetti code.</p>
|
||||
|
||||
<p>The next time you open a controller and find 700 lines of intertwined logic, remember: it's not your fault. But it is your responsibility to fix it. And the fix has a name: CQRS + MediatR, inside a Vertical Slice Architecture. No more spaghetti code. Just clean, maintainable, testable .NET applications that make you proud to call yourself a developer.</p>
|
||||
|
||||
</article>
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200 rounded-2xl"><p class="text-sm text-gray-700 font-semibold mb-2">Explore VSA + CQRS + MediatR in Production</p><p class="text-sm text-gray-600 mb-4">See how Indotalent products implement this exact architecture. Zero spaghetti code. Complete source — $21 each.</p><a href="../index.html#products" class="btn-primary text-sm">Explore Products</a></div>
|
||||
<div class="mt-6"><a href="index.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div></section>
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html></content>
|
||||
<content>Write file to 'blog/cqrs-mediatr-no-spaghetti-code.html'.
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Blog">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script>
|
||||
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Blog — .NET 10, Blazor & Vertical Slice Architecture | Indotalent</title>
|
||||
<meta name="description" content="Technical articles on .NET 10, Blazor Server, Vertical Slice Architecture (VSA), MudBlazor, and enterprise source code. Real-world case studies for developers." />
|
||||
<meta name="keywords" content="Vertical Slice Architecture, VSA, Blazor Server, .NET 10, MudBlazor, C#, enterprise source code, ASP.NET Core, MediatR" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/" />
|
||||
<link rel="alternate" hreflang="en" href="https://www.indotalent.com/blog/" />
|
||||
<link rel="alternate" hreflang="x-default" href="https://www.indotalent.com/blog/" />
|
||||
<meta property="og:title" content="Blog — .NET 10, Blazor & VSA | Indotalent" />
|
||||
<meta property="og:description" content="Technical case studies and deep dives into Vertical Slice Architecture, Blazor Server, and .NET 10 development." />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://www.indotalent.com/" },
|
||||
{ "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://www.indotalent.com/blog/" }
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>
|
||||
.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
|
||||
.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
|
||||
.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}
|
||||
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 20px -4px rgba(249,115,22,0.45)}
|
||||
.blog-card{background:#fff;border:1px solid #e5e7eb;border-radius:16px;padding:24px;transition:all 0.3s ease}
|
||||
.blog-card:hover{transform:translateY(-4px);box-shadow:0 16px 40px -12px rgba(249,115,22,0.15);border-color:#f97316}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100">
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5">
|
||||
<ol class="flex items-center gap-1.5 text-xs text-gray-400">
|
||||
<li><a href="../index.html" class="hover:text-brand-500 transition-colors">Home</a></li>
|
||||
<li><span class="text-gray-300">/</span></li>
|
||||
<li><span class="text-gray-600 font-medium">Blog</span></li>
|
||||
</ol>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div>
|
||||
</nav>
|
||||
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12">
|
||||
<div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="text-center mb-12">
|
||||
<div class="inline-flex items-center gap-1.5 bg-orange-50 border border-orange-200 rounded-full px-3.5 py-1 mb-5"><span class="text-brand-700 text-xs font-medium tracking-wide">Technical Blog</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl lg:text-5xl font-extrabold text-gray-900 mb-4">Blog: <span class="gradient-text">.NET 10, Blazor & VSA</span></h1>
|
||||
<p class="text-gray-500 max-w-xl mx-auto">Production-proven patterns for building enterprise .NET applications. Deep dives, case studies, and practical guides.</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<a href="vsa-introduction.html" class="blog-card block">
|
||||
<div class="flex items-center gap-2 mb-2"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">June 2026 · 8 min read</span></div>
|
||||
<h2 class="text-lg font-bold text-gray-900 mb-1">Introduction to Vertical Slice Architecture in .NET 10</h2>
|
||||
<p class="text-sm text-gray-500">How VSA organizes code by business features, why it beats layered architecture, and a concrete .NET 10 Minimal API example.</p>
|
||||
</a>
|
||||
<a href="blazor-hosting-model.html" class="blog-card block">
|
||||
<div class="flex items-center gap-2 mb-2"><span class="text-xs font-semibold bg-blue-50 text-blue-700 border border-blue-200 rounded-full px-2.5 py-0.5">Blazor Server</span><span class="text-xs text-gray-400">June 2026 · 7 min read</span></div>
|
||||
<h2 class="text-lg font-bold text-gray-900 mb-1">Blazor Server vs WebAssembly: Choosing the Right Hosting Model</h2>
|
||||
<p class="text-sm text-gray-500">Compare Blazor Server and WASM for enterprise applications. Why Indotalent products use Blazor Server.</p>
|
||||
</a>
|
||||
<a href="vsa-eliminates-project-hell.html" class="blog-card block">
|
||||
<div class="flex items-center gap-2 mb-2"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">May 2026 · 6 min read</span></div>
|
||||
<h2 class="text-lg font-bold text-gray-900 mb-1">How Vertical Slice Architecture Eliminates "Project Hell"</h2>
|
||||
<p class="text-sm text-gray-500">From 15+ projects to a single monolith. How VSA keeps related code together and slashes compilation time.</p>
|
||||
</a>
|
||||
<a href="vsa-mediatr.html" class="blog-card block">
|
||||
<div class="flex items-center gap-2 mb-2"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">May 2026 · 9 min read</span></div>
|
||||
<h2 class="text-lg font-bold text-gray-900 mb-1">Building Maintainable .NET Applications with MediatR and VSA</h2>
|
||||
<p class="text-sm text-gray-500">How MediatR enables CQRS within VSA slices. Pipeline behaviors for validation, logging, and transactions.</p>
|
||||
</a>
|
||||
<a href="mudblazor-component-library.html" class="blog-card block">
|
||||
<div class="flex items-center gap-2 mb-2"><span class="text-xs font-semibold bg-purple-50 text-purple-700 border border-purple-200 rounded-full px-2.5 py-0.5">MudBlazor</span><span class="text-xs text-gray-400">April 2026 · 5 min read</span></div>
|
||||
<h2 class="text-lg font-bold text-gray-900 mb-1">MudBlazor: The Ultimate Component Library for Blazor Server</h2>
|
||||
<p class="text-sm text-gray-500">50+ Material Design components. Why MudBlazor is the de facto standard for Blazor enterprise apps.</p>
|
||||
</a>
|
||||
<a href="blazor-performance-optimization.html" class="blog-card block">
|
||||
<div class="flex items-center gap-2 mb-2"><span class="text-xs font-semibold bg-blue-50 text-blue-700 border border-blue-200 rounded-full px-2.5 py-0.5">Blazor Server</span><span class="text-xs text-gray-400">April 2026 · 7 min read</span></div>
|
||||
<h2 class="text-lg font-bold text-gray-900 mb-1">Blazor Server Performance Optimization: Real-World Techniques</h2>
|
||||
<p class="text-sm text-gray-500">ShouldRender, virtualization, no-tracking queries, and server-side pagination for Blazor Server apps.</p>
|
||||
</a>
|
||||
<a href="clean-architecture-to-vsa-migration.html" class="blog-card block">
|
||||
<div class="flex items-center gap-2 mb-2"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">March 2026 · 10 min read</span></div>
|
||||
<h2 class="text-lg font-bold text-gray-900 mb-1">From Clean Architecture to Vertical Slices: A Practical Migration Guide</h2>
|
||||
<p class="text-sm text-gray-500">Step-by-step approach to migrate from layered architecture to VSA, one feature at a time.</p>
|
||||
</a>
|
||||
<a href="blazor-server-security-jwt.html" class="blog-card block">
|
||||
<div class="flex items-center gap-2 mb-2"><span class="text-xs font-semibold bg-blue-50 text-blue-700 border border-blue-200 rounded-full px-2.5 py-0.5">Blazor Server</span><span class="text-xs text-gray-400">March 2026 · 8 min read</span></div>
|
||||
<h2 class="text-lg font-bold text-gray-900 mb-1">Securing Your Blazor Server Application with JWT and ASP.NET Core Identity</h2>
|
||||
<p class="text-sm text-gray-500">Four-layer security: Identity, JWT, policy-based authorization, and SignalR protection.</p>
|
||||
</a>
|
||||
<a href="vsa-vs-layered-architecture.html" class="blog-card block">
|
||||
<div class="flex items-center gap-2 mb-2"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">Feb 2026 · 8 min read</span></div>
|
||||
<h2 class="text-lg font-bold text-gray-900 mb-1">Vertical Slice Architecture vs Traditional Layered Architecture: Side-by-Side Code</h2>
|
||||
<p class="text-sm text-gray-500">7 files in 4 projects vs 1 file. A concrete code comparison of both approaches.</p>
|
||||
</a>
|
||||
<a href="real-world-vsa-net10.html" class="blog-card block">
|
||||
<div class="flex items-center gap-2 mb-2"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">Feb 2026 · 6 min read</span></div>
|
||||
<h2 class="text-lg font-bold text-gray-900 mb-1">Real-World VSA: Shipping Production-Ready .NET 10 Apps with Indotalent</h2>
|
||||
<p class="text-sm text-gray-500">How each Indotalent product is built. The stack, the architecture, and what you get for $21.</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-12 bg-gradient-to-r from-orange-50 via-amber-50 to-orange-50 border border-orange-200 rounded-2xl p-8 text-center">
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-3">Ready to Apply These Patterns?</h2>
|
||||
<p class="text-gray-600 mb-6">Every Indotalent product is built with VSA, Blazor Server, and MudBlazor. Complete source code — $21.</p>
|
||||
<a href="../index.html#products" class="btn-primary text-base">Explore Products</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><div class="flex flex-wrap gap-4 text-sm"><a href="../index.html#products" class="text-gray-500 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-gray-500 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-gray-500 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-brand-600 font-medium">Blog</a><a href="../support.html" class="text-gray-500 hover:text-brand-500">Support</a></div><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>MudBlazor: The Ultimate Component Library for Blazor Server | Indotalent Blog</title>
|
||||
<meta name="description" content="50+ Material Design components. Why MudBlazor is the de facto standard for Blazor enterprise apps. Pure C# components with native two-way binding." />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/mudblazor-component-library.html" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"Article","headline":"MudBlazor: The Ultimate Component Library for Blazor Server","author":{"@type":"Person","name":"go2ismail"},"datePublished":"2026-04-01","publisher":{"@type":"Organization","name":"Indotalent"}}</script>
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{ "@type":"ListItem","position":1,"name":"Home","item":"https://www.indotalent.com/" },{ "@type":"ListItem","position":2,"name":"Blog","item":"https://www.indotalent.com/blog/" },{ "@type":"ListItem","position":3,"name":"MudBlazor Component Library","item":"https://www.indotalent.com/blog/mudblazor-component-library.html" }]}</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100"><div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5"><ol class="flex items-center gap-1.5 text-xs text-gray-400"><li><a href="../index.html" class="hover:text-brand-500">Home</a></li><li><span class="text-gray-300">/</span></li><li><a href="index.html" class="hover:text-brand-500">Blog</a></li><li><span class="text-gray-300">/</span></li><li><span class="text-gray-600 font-medium">MudBlazor</span></li></ol></div></nav>
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div></nav>
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12"><div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3"><span class="text-xs font-semibold bg-purple-50 text-purple-700 border border-purple-200 rounded-full px-2.5 py-0.5">MudBlazor</span><span class="text-xs text-gray-400">April 2026 · 5 min read</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">MudBlazor: The Ultimate Component Library for Blazor Server</h1>
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-8"><p class="text-sm text-green-800 font-semibold mb-1">TL;DR</p><p class="text-sm text-green-700">MudBlazor provides 50+ Material Design components written in pure C# and Razor. With native two-way binding and a unified theming system, the <code>MudDataGrid</code> alone saves days of development.</p></div>
|
||||
<article>
|
||||
<p>MudBlazor has become the de facto standard component library for Blazor enterprise applications. It provides over 50 Material Design components — from simple buttons and inputs to complex data grids, charts, and date pickers — all designed specifically for Blazor's component model.</p>
|
||||
<h2>Why MudBlazor Wins</h2>
|
||||
<p>Unlike wrapping JavaScript libraries, MudBlazor components are written in pure C# and Razor. This means two-way binding works natively, event callbacks are type-safe, and components compose naturally through cascading parameters. The <code>MudDataGrid</code> alone handles sorting, filtering, pagination, and inline editing with minimal configuration — functionality that would take days to build from scratch. For enterprise apps like CRM or WMS systems that are data-grid heavy, MudBlazor's grid component is a massive productivity multiplier.</p>
|
||||
<h2>Consistent Theming</h2>
|
||||
<p>MudBlazor's theming system provides consistent typography, spacing, and color across all components. You define your brand colors once in <code>MudTheme</code>, and every component respects it. This ensures the 50+ pages in a typical Indotalent product look cohesive without writing custom CSS for each page.</p>
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul><li>50+ components written in pure C# — no JavaScript wrappers</li><li>MudDataGrid handles sorting, filtering, pagination, and inline editing out of the box</li><li>Unified theming ensures visual consistency across entire applications</li><li>All Indotalent products are built with MudBlazor 9</li></ul>
|
||||
</article>
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200 rounded-2xl"><p class="text-sm text-gray-700 font-semibold mb-2">See MudBlazor in production</p><p class="text-sm text-gray-600 mb-4">Every Indotalent product uses MudBlazor for its UI. $21 each.</p><a href="../index.html#products" class="btn-primary text-sm">Explore Products</a></div>
|
||||
<div class="mt-6"><a href="index.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div></section>
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Real-World VSA: Shipping Production-Ready .NET 10 Apps with Indotalent | Indotalent Blog</title>
|
||||
<meta name="description" content="How each Indotalent product is built. The complete stack, the architecture, and what you get for $21. Production-ready VSA source code with 50-100 feature slices each." />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/real-world-vsa-net10.html" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"Article","headline":"Real-World VSA: Shipping Production-Ready .NET 10 Apps with Indotalent","author":{"@type":"Person","name":"go2ismail"},"datePublished":"2026-02-01","publisher":{"@type":"Organization","name":"Indotalent"}}</script>
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{ "@type":"ListItem","position":1,"name":"Home","item":"https://www.indotalent.com/" },{ "@type":"ListItem","position":2,"name":"Blog","item":"https://www.indotalent.com/blog/" },{ "@type":"ListItem","position":3,"name":"Real-World VSA","item":"https://www.indotalent.com/blog/real-world-vsa-net10.html" }]}</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script><script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}article h3{font-size:1.15rem;font-weight:600;margin:1.5rem 0 0.5rem}article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100"><div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5"><ol class="flex items-center gap-1.5 text-xs text-gray-400"><li><a href="../index.html" class="hover:text-brand-500">Home</a></li><li><span class="text-gray-300">/</span></li><li><a href="index.html" class="hover:text-brand-500">Blog</a></li><li><span class="text-gray-300">/</span></li><li><span class="text-gray-600 font-medium">Real-World VSA</span></li></ol></div></nav>
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div></nav>
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12"><div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">Feb 2026 · 6 min read</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">Real-World VSA: Shipping Production-Ready .NET 10 Apps with Indotalent</h1>
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-8"><p class="text-sm text-green-800 font-semibold mb-1">TL;DR</p><p class="text-sm text-green-700">Every Indotalent product is a complete .NET 10 application with 50-100 VSA feature slices, ready to deploy or customize. Clone, configure connection string, press F5 — running in 60 seconds.</p></div>
|
||||
<article>
|
||||
<p>Every Indotalent product is a complete, production-ready .NET 10 application built on Vertical Slice Architecture. These aren't templates or starter kits — they're full enterprise applications with 50-100 feature slices each, ready to deploy or customize.</p>
|
||||
<h2>The Production Stack</h2>
|
||||
<p>Each product runs on an identical, battle-tested stack: <strong>.NET 10 + ASP.NET Core 10 Minimal APIs</strong> for the backend, <strong>Blazor Server + MudBlazor 9</strong> for the UI, <strong>MediatR</strong> for CQRS within VSA slices, <strong>FluentValidation</strong> for request validation, <strong>Entity Framework Core + SQL Server</strong> for data access with migrations and seed data, and <strong>JWT + ASP.NET Core Identity</strong> for authentication and role-based authorization.</p>
|
||||
<h2>What You Get</h2>
|
||||
<p>A single Visual Studio solution that compiles in seconds. You clone the repo, configure your SQL Server connection string, run EF Core migrations (which automatically seed demo data), and press F5. The entire application — backend API, Blazor Server UI, database — runs locally in under 60 seconds from clone to working app.</p>
|
||||
<h2>The Price</h2>
|
||||
<p>Every product is priced at $21 (regularly $49) — accessible pricing for individual developers, freelancers, and small agencies. You get the complete, unrestricted source code with lifetime access. Study the architecture, extend it for clients, or deploy it as-is. Your purchase directly supports ongoing open-source development and keeps these products affordable for the .NET community.</p>
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul><li>9 complete .NET 10 applications, each with 50-100 VSA feature slices</li><li>Identical production stack across all products for skill transferability</li><li>Clone → configure connection string → F5 = running app in 60 seconds</li><li>$21 each, lifetime access, complete unrestricted source code</li></ul>
|
||||
</article>
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200 rounded-2xl"><p class="text-sm text-gray-700 font-semibold mb-2">Get production-ready VSA source code</p><p class="text-sm text-gray-600 mb-4">9 products. Complete source code. $21 each.</p><a href="../index.html#products" class="btn-primary text-sm">Explore Products</a></div>
|
||||
<div class="mt-6"><a href="index.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div></section>
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,196 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script>
|
||||
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Why Vertical Slice Architecture is the Best Architecture for AI-Assisted Development | Indotalent Blog</title>
|
||||
<meta name="description" content="VSA organizes code by feature in a single folder end-to-end. This makes AI tools like GitHub Copilot and ChatGPT dramatically more effective by keeping context focused in one place. Stop jumping between 7 projects." />
|
||||
<meta name="keywords" content="VSA AI-assisted development, Vertical Slice Architecture AI, GitHub Copilot VSA, AI coding best architecture, AI-friendly code structure, Cursor AI .NET, ChatGPT code generation architecture" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/vsa-ai-assisted-development.html" />
|
||||
<link rel="alternate" hreflang="en" href="https://www.indotalent.com/blog/vsa-ai-assisted-development.html" />
|
||||
<link rel="alternate" hreflang="x-default" href="https://www.indotalent.com/blog/vsa-ai-assisted-development.html" />
|
||||
<meta property="og:title" content="Why Vertical Slice Architecture is the Best Architecture for AI-Assisted Development" />
|
||||
<meta property="og:description" content="VSA organizes code by feature end-to-end in a single folder — the perfect context window for AI tools." />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Article",
|
||||
"headline": "Why Vertical Slice Architecture is the Best Architecture for AI-Assisted Development",
|
||||
"description": "How VSA's feature-centric organization maximizes AI coding tool effectiveness by keeping all context for a feature in one folder — no jumping between 7 projects.",
|
||||
"author": { "@type": "Person", "name": "go2ismail" },
|
||||
"datePublished": "2026-07-01",
|
||||
"publisher": { "@type": "Organization", "name": "Indotalent" }
|
||||
}
|
||||
</script>
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://www.indotalent.com/" },
|
||||
{ "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://www.indotalent.com/blog.html" },
|
||||
{ "@type": "ListItem", "position": 3, "name": "VSA for AI-Assisted Development", "item": "https://www.indotalent.com/blog/vsa-ai-assisted-development.html" }
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>
|
||||
.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
|
||||
.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
|
||||
.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}
|
||||
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 20px -4px rgba(249,115,22,0.45)}
|
||||
article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}
|
||||
article h3{font-size:1.15rem;font-weight:600;margin:1.5rem 0 0.5rem;color:#1f2937}
|
||||
article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}
|
||||
article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}
|
||||
article pre{background:#1f2937;color:#f9fafb;padding:16px;border-radius:10px;overflow-x:auto;margin:1rem 0;font-size:0.85rem;line-height:1.6}
|
||||
article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}
|
||||
article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}
|
||||
.highlight-box{background:linear-gradient(135deg,#fef3c7,#fde68a);border:1px solid #f59e0b;border-radius:12px;padding:20px;margin:1.5rem 0}
|
||||
.comparison-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin:1.5rem 0}
|
||||
.bad{background:#fef2f2;border:1px solid #fecaca;border-radius:12px;padding:16px}
|
||||
.good{background:#f0fdf4;border:1px solid #bbf7d0;border-radius:12px;padding:16px}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100">
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5">
|
||||
<ol class="flex items-center gap-1.5 text-xs text-gray-400">
|
||||
<li><a href="../index.html" class="hover:text-brand-500 transition-colors">Home</a></li>
|
||||
<li><span class="text-gray-300">/</span></li>
|
||||
<li><a href="../blog.html" class="hover:text-brand-500 transition-colors">Blog</a></li>
|
||||
<li><span class="text-gray-300">/</span></li>
|
||||
<li><span class="text-gray-600 font-medium">VSA & AI-Assisted Development</span></li>
|
||||
</ol>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="../blog.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div>
|
||||
</nav>
|
||||
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12">
|
||||
<div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<span class="text-xs font-semibold bg-violet-50 text-violet-700 border border-violet-200 rounded-full px-2.5 py-0.5">AI + VSA</span>
|
||||
<span class="text-xs text-gray-400">July 2026 · 9 min read</span>
|
||||
</div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">Why Vertical Slice Architecture is the Best Architecture for AI-Assisted Development</h1>
|
||||
|
||||
<div class="bg-violet-50 border border-violet-200 rounded-xl p-4 mb-8">
|
||||
<p class="text-sm text-violet-800 font-semibold mb-1">TL;DR</p>
|
||||
<p class="text-sm text-violet-700">VSA organizes every feature — from API endpoint to database query — <strong>in a single folder</strong>. This means AI tools like GitHub Copilot, Cursor, and ChatGPT have <strong>all the context they need in one place</strong>. No jumping between 7 projects. No missing abstractions. Just one folder, one feature, one perfect context window for AI. This is the architecture that makes AI-assisted development <strong>actually work</strong>.</p>
|
||||
</div>
|
||||
|
||||
<article>
|
||||
<h2>The Problem: AI Doesn't Understand Your Project Structure</h2>
|
||||
<p>Here's what happens when you use AI coding tools with traditional Clean Architecture:</p>
|
||||
<p>You ask Copilot to "add a validation rule to the Create Order endpoint." Copilot looks at the file you're in — maybe <code>CreateOrderCommand.cs</code> in the <code>Application</code> project. It generates code. But the code is wrong because <strong>Copilot can't see</strong> that the validation should also check a business rule in the <code>Domain</code> project, a permission defined in the <code>Infrastructure</code> project, and an existing query in the <code>Persistence</code> project. Those are in <strong>different folders, different projects, different context windows</strong>.</p>
|
||||
<p>You end up playing ping-pong with the AI: "No, also check this..." — "Wait, there's another rule in..." — "The repository interface is actually..." Each round-trip is a context switch. Each context switch is a chance for the AI to hallucinate.</p>
|
||||
|
||||
<div class="highlight-box">
|
||||
<p class="text-sm font-semibold text-amber-900 mb-2">🔑 The Core Insight</p>
|
||||
<p class="text-sm text-amber-800">AI tools work best when <strong>all relevant code is visible within a single context window</strong>. VSA puts all feature code in one folder. Traditional architecture scatters it across 5-7 projects. Guess which one produces better AI-generated code?</p>
|
||||
</div>
|
||||
|
||||
<h2>How VSA Makes AI 10x More Effective</h2>
|
||||
|
||||
<div class="comparison-grid">
|
||||
<div class="bad">
|
||||
<h3 class="text-base font-semibold text-red-800 mb-2">❌ Layered Architecture + AI</h3>
|
||||
<ul class="text-sm text-red-700 space-y-1" style="list-style:none;padding:0">
|
||||
<li>• AI sees 1 of 7 files in context</li>
|
||||
<li>• Generated code misses cross-project dependencies</li>
|
||||
<li>• Developer must manually assemble context</li>
|
||||
<li>• Every prompt needs 5+ files as reference</li>
|
||||
<li>• High hallucination rate</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="good">
|
||||
<h3 class="text-base font-semibold text-emerald-800 mb-2">✅ VSA + AI</h3>
|
||||
<ul class="text-sm text-emerald-700 space-y-1" style="list-style:none;padding:0">
|
||||
<li>• AI sees endpoint + handler + DTOs + queries</li>
|
||||
<li>• Single file has complete feature context</li>
|
||||
<li>• Developer opens 1 file, AI understands everything</li>
|
||||
<li>• One prompt = complete feature modification</li>
|
||||
<li>• Dramatically lower hallucination rate</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>A Real Scenario: Adding a Field with AI</h2>
|
||||
<p>Let's walk through a concrete example. You need to add a <code>DiscountCode</code> field to the Create Order feature.</p>
|
||||
|
||||
<h3>In Layered Architecture (The Painful Way)</h3>
|
||||
<ol style="list-style:decimal;padding-left:1.5rem;margin-bottom:1rem">
|
||||
<li style="font-size:0.95rem;line-height:1.8;color:#4b5563">Open <code>Controllers/OrdersController.cs</code> — tell AI about the endpoint</li>
|
||||
<li style="font-size:0.95rem;line-height:1.8;color:#4b5563">Open <code>Application/Orders/CreateOrderCommand.cs</code> — tell AI about the DTO</li>
|
||||
<li style="font-size:0.95rem;line-height:1.8;color:#4b5563">Open <code>Application/Orders/CreateOrderHandler.cs</code> — tell AI about the business logic</li>
|
||||
<li style="font-size:0.95rem;line-height:1.8;color:#4b5563">Open <code>Domain/Entities/Order.cs</code> — tell AI about the domain model</li>
|
||||
<li style="font-size:0.95rem;line-height:1.8;color:#4b5563">Open <code>Infrastructure/Persistence/AppDbContext.cs</code> — tell AI about the EF configuration</li>
|
||||
<li style="font-size:0.95rem;line-height:1.8;color:#4b5563">Run EF Core migration — another context switch</li>
|
||||
</ol>
|
||||
<p><strong>7 files. 4 projects. 6+ AI prompts.</strong> Each prompt is a new context window. Each context window is a chance for the AI to forget what you told it 3 prompts ago. This is why AI-generated code in layered projects is notoriously unreliable.</p>
|
||||
|
||||
<h3>In Vertical Slice Architecture (The Elegant Way)</h3>
|
||||
<ol style="list-style:decimal;padding-left:1.5rem;margin-bottom:1rem">
|
||||
<li style="font-size:0.95rem;line-height:1.8;color:#4b5563">Open <code>Features/Orders/CreateOrder.cs</code></li>
|
||||
<li style="font-size:0.95rem;line-height:1.8;color:#4b5563">Tell AI: "Add a <code>DiscountCode</code> string property. Validate it's max 20 chars. Save it to the Order entity. Return it in the response DTO."</li>
|
||||
<li style="font-size:0.95rem;line-height:1.8;color:#4b5563">AI sees the endpoint, command, handler, and validation — all in one file. Generates correct code in <strong>one shot</strong>.</li>
|
||||
</ol>
|
||||
<p><strong>1 file. 1 folder. 1 prompt.</strong> This is the difference between fighting your AI tools and <strong>flowing with them</strong>.</p>
|
||||
|
||||
<h2>The Context Window is Everything</h2>
|
||||
<p>Modern AI coding assistants — GitHub Copilot, Cursor, Claude, ChatGPT — all operate on a context window. They can "see" the file you're editing plus some surrounding context (open tabs, recently viewed files). The quality of their output is <strong>directly proportional to the quality and completeness of that context</strong>.</p>
|
||||
<p>VSA maximizes context quality by design:</p>
|
||||
<ul>
|
||||
<li><strong>Endpoint + Handler + DTOs in one file:</strong> The AI sees the HTTP contract, business logic, and data shapes simultaneously. It won't generate a handler that returns a DTO the endpoint can't serialize.</li>
|
||||
<li><strong>Validation in the same folder:</strong> The AI knows the validation rules exist. It won't generate business logic that bypasses validation.</li>
|
||||
<li><strong>EF Core queries inline:</strong> The AI sees exactly how data is fetched. It won't hallucinate repository methods that don't exist.</li>
|
||||
</ul>
|
||||
|
||||
<h2>AI-Friendly Means Developer-Friendly</h2>
|
||||
<p>Here's the beautiful symmetry: what makes code AI-friendly is <strong>exactly</strong> what makes it human-friendly. Maximum cohesion. Minimum scattering. One concept per folder. No abstractions between you and the business logic.</p>
|
||||
<p>When a new developer joins your team and you say "add a discount code to order creation," they should be able to do it by reading <strong>one file</strong>. That same principle applies to AI tools. The architecture that's best for humans is also best for machines. VSA delivers both.</p>
|
||||
|
||||
<h2>Indotalent Products: Built for the AI Era</h2>
|
||||
<p>Every Indotalent product is a single .NET 10 Blazor Server project built with Vertical Slice Architecture. Each feature — 50 to 100 per application — lives in its own folder <code>Features/[FeatureName]/</code> with its endpoint, MediatR handler, FluentValidation validator, and EF Core queries all in one place.</p>
|
||||
<p>This means:</p>
|
||||
<ul>
|
||||
<li><strong>GitHub Copilot</strong> generates accurate code because all context is in one file</li>
|
||||
<li><strong>Cursor AI</strong> understands complete features in a single tab</li>
|
||||
<li><strong>ChatGPT & Claude</strong> can be given one file as reference and generate correct modifications</li>
|
||||
<li><strong>You spend less time debugging AI hallucinations</strong> and more time shipping features</li>
|
||||
</ul>
|
||||
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul>
|
||||
<li>VSA keeps all feature code in one folder — the <strong>perfect context window</strong> for AI tools</li>
|
||||
<li>Layered architecture forces AI to work with fragmented context → high hallucination rate</li>
|
||||
<li>Adding a field in VSA: 1 file, 1 prompt. In layered: 7 files, 6+ prompts</li>
|
||||
<li>AI-friendly architecture = human-friendly architecture = <strong>Vertical Slice Architecture</strong></li>
|
||||
<li>All 9 Indotalent products are VSA monoliths — ready for AI-assisted customization</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-violet-50 to-purple-50 border border-violet-200 rounded-2xl">
|
||||
<p class="text-sm text-gray-900 font-semibold mb-2">Stop fighting your AI tools. Start using VSA.</p>
|
||||
<p class="text-sm text-gray-600 mb-4">Every Indotalent product is a complete VSA application — the perfect foundation for AI-assisted development. <strong>$21 each.</strong></p>
|
||||
<a href="../index.html#products" class="btn-primary text-sm">Explore Products</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-6"><a href="../blog.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script>
|
||||
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>How Vertical Slice Architecture Eliminates "Project Hell" | Indotalent Blog</title>
|
||||
<meta name="description" content="From 15+ projects to a single monolith. How VSA keeps related code together, slashes compilation time, and makes onboarding instant." />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/vsa-eliminates-project-hell.html" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">
|
||||
{ "@context":"https://schema.org","@type":"Article","headline":"How Vertical Slice Architecture Eliminates Project Hell","author":{"@type":"Person","name":"go2ismail"},"datePublished":"2026-05-01","publisher":{"@type":"Organization","name":"Indotalent"}}
|
||||
</script>
|
||||
<script type="application/ld+json">
|
||||
{ "@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{ "@type":"ListItem","position":1,"name":"Home","item":"https://www.indotalent.com/" },{ "@type":"ListItem","position":2,"name":"Blog","item":"https://www.indotalent.com/blog/" },{ "@type":"ListItem","position":3,"name":"VSA Eliminates Project Hell","item":"https://www.indotalent.com/blog/vsa-eliminates-project-hell.html" }]}
|
||||
</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 20px -4px rgba(249,115,22,0.45)}article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}article h3{font-size:1.15rem;font-weight:600;margin:1.5rem 0 0.5rem}article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100"><div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5"><ol class="flex items-center gap-1.5 text-xs text-gray-400"><li><a href="../index.html" class="hover:text-brand-500">Home</a></li><li><span class="text-gray-300">/</span></li><li><a href="index.html" class="hover:text-brand-500">Blog</a></li><li><span class="text-gray-300">/</span></li><li><span class="text-gray-600 font-medium">VSA Eliminates Project Hell</span></li></ol></div></nav>
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div></nav>
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12"><div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">May 2026 · 6 min read</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">How Vertical Slice Architecture Eliminates "Project Hell"</h1>
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-8"><p class="text-sm text-green-800 font-semibold mb-1">TL;DR</p><p class="text-sm text-green-700">Organizing by technical concern scatters related code across the codebase. VSA reorganizes around business capabilities. Every Indotalent product is a single .NET project where all code for a feature lives in one folder.</p></div>
|
||||
<article>
|
||||
<p>I've worked on enterprise .NET solutions with 15+ projects in a single solution. The cognitive overhead was staggering. To implement a new feature, you'd touch files in <code>Application/</code>, <code>Domain/</code>, <code>Infrastructure/</code>, <code>Persistence/</code>, <code>Web/</code>, <code>SharedKernel/</code>, and <code>Contracts/</code>. Compilation times climbed past 30 seconds. New developers took weeks to become productive. This is what I call "Project Hell."</p>
|
||||
<h2>The Root Cause</h2>
|
||||
<p>The problem isn't Clean Architecture's principles — dependency inversion, separation of concerns, and testability are all good things. The problem is that organizing by technical concern (all controllers here, all services there) scatters related code across the codebase. When a feature changes, you must hunt across multiple projects to understand the impact.</p>
|
||||
<h2>The VSA Solution</h2>
|
||||
<p>VSA reorganizes the same principles around business capabilities. Every Indotalent product is a single .NET project. All code for a feature lives in one folder. The API endpoint, MediatR handler, validation, and EF Core queries sit together. Compilation takes seconds. Adding a feature means creating one folder with one file. Need to understand how Sales Orders work? Open <code>Features/SalesOrders/</code> and read through. Everything is there.</p>
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul><li>Layered architecture scatters related code across 5-15 projects</li><li>Single-project VSA monolith compiles in seconds with zero cognitive overhead</li><li>New developers become productive on day one by reading one feature slice</li><li>All 9 Indotalent products demonstrate this architecture in production code</li></ul>
|
||||
</article>
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200 rounded-2xl"><p class="text-sm text-gray-700 font-semibold mb-2">See Single-Project VSA in action</p><p class="text-sm text-gray-600 mb-4">Every Indotalent product is a single .NET project with VSA. $21 each.</p><a href="../index.html#products" class="btn-primary text-sm">Explore Products</a></div>
|
||||
<div class="mt-6"><a href="index.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div></section>
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,147 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script>
|
||||
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Introduction to Vertical Slice Architecture in .NET 10 | Indotalent Blog</title>
|
||||
<meta name="description" content="Learn how Vertical Slice Architecture (VSA) organizes .NET 10 code by business features instead of technical layers. Complete with Minimal API code examples." />
|
||||
<meta name="keywords" content="Vertical Slice Architecture, VSA .NET 10, Minimal API, MediatR, CQRS, enterprise architecture, .NET architecture patterns" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/vsa-introduction.html" />
|
||||
<link rel="alternate" hreflang="en" href="https://www.indotalent.com/blog/vsa-introduction.html" />
|
||||
<link rel="alternate" hreflang="x-default" href="https://www.indotalent.com/blog/vsa-introduction.html" />
|
||||
<meta property="og:title" content="Introduction to Vertical Slice Architecture in .NET 10" />
|
||||
<meta property="og:description" content="Learn how VSA organizes .NET 10 code by business features. Complete with code examples." />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Article",
|
||||
"headline": "Introduction to Vertical Slice Architecture in .NET 10",
|
||||
"description": "How VSA organizes code by business features, why it beats layered architecture, and concrete .NET 10 Minimal API examples.",
|
||||
"author": { "@type": "Person", "name": "go2ismail" },
|
||||
"datePublished": "2026-06-01",
|
||||
"publisher": { "@type": "Organization", "name": "Indotalent" }
|
||||
}
|
||||
</script>
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://www.indotalent.com/" },
|
||||
{ "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://www.indotalent.com/blog/" },
|
||||
{ "@type": "ListItem", "position": 3, "name": "Introduction to Vertical Slice Architecture in .NET 10", "item": "https://www.indotalent.com/blog/vsa-introduction.html" }
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>
|
||||
.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
|
||||
.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
|
||||
.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}
|
||||
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 20px -4px rgba(249,115,22,0.45)}
|
||||
article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}
|
||||
article h3{font-size:1.15rem;font-weight:600;margin:1.5rem 0 0.5rem;color:#1f2937}
|
||||
article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}
|
||||
article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}
|
||||
article pre{background:#1f2937;color:#f9fafb;padding:16px;border-radius:10px;overflow-x:auto;margin:1rem 0;font-size:0.85rem;line-height:1.6}
|
||||
article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}
|
||||
article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100">
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5">
|
||||
<ol class="flex items-center gap-1.5 text-xs text-gray-400">
|
||||
<li><a href="../index.html" class="hover:text-brand-500 transition-colors">Home</a></li>
|
||||
<li><span class="text-gray-300">/</span></li>
|
||||
<li><a href="index.html" class="hover:text-brand-500 transition-colors">Blog</a></li>
|
||||
<li><span class="text-gray-300">/</span></li>
|
||||
<li><span class="text-gray-600 font-medium">VSA Introduction</span></li>
|
||||
</ol>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div>
|
||||
</nav>
|
||||
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12">
|
||||
<div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">June 2026 · 8 min read</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">Introduction to Vertical Slice Architecture in .NET 10</h1>
|
||||
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-8">
|
||||
<p class="text-sm text-green-800 font-semibold mb-1">TL;DR</p>
|
||||
<p class="text-sm text-green-700">Vertical Slice Architecture organizes code by business features instead of technical layers. In .NET 10, you can define an entire feature — endpoint, DTOs, validation, and handler — in <strong>a single C# file</strong>. This maximizes cohesion, reduces cognitive overhead, and lets you add features without touching 7 different projects.</p>
|
||||
</div>
|
||||
|
||||
<article>
|
||||
<p>Vertical Slice Architecture (VSA) is a software design approach that organizes code by business features rather than technical layers. In a traditional layered architecture, implementing a "Create Order" feature means working across <code>Controllers/</code>, <code>Services/</code>, <code>Repositories/</code>, <code>Domain/</code>, and <code>DTOs/</code> — often spread across 5+ separate projects. VSA flips this on its head: everything for "Create Order" lives in one folder, one file, or at most a small cluster of files within a single project.</p>
|
||||
|
||||
<h2>Why VSA Matters in .NET 10</h2>
|
||||
<p>.NET 10's Minimal APIs make VSA more natural than ever. You can define an entire feature — endpoint, request/response DTOs, validation, and handler logic — in a single C# file using top-level statements and MediatR. The result is maximum cohesion: everything you need to understand a feature is in one place. No more jumping between 7 projects in Solution Explorer just to trace a single user action.</p>
|
||||
|
||||
<h3>Key Benefits</h3>
|
||||
<ul>
|
||||
<li><strong>Maximum Cohesion:</strong> All code for a feature lives together. When you need to modify "Create Order," you open one file.</li>
|
||||
<li><strong>Faster Onboarding:</strong> New developers understand a feature by reading a single file, not tracing through 5 projects.</li>
|
||||
<li><strong>Reduced Merge Conflicts:</strong> Teams working on different features rarely touch the same files.</li>
|
||||
<li><strong>Easier Testing:</strong> Each slice is self-contained and can be tested in isolation.</li>
|
||||
</ul>
|
||||
|
||||
<h2>A Concrete Example</h2>
|
||||
<pre>// Features/Orders/CreateOrder.cs
|
||||
public static class CreateOrderEndpoint
|
||||
{
|
||||
public static void Map(IEndpointRouteBuilder app) =>
|
||||
app.MapPost("/api/orders", async (CreateOrderCommand cmd, IMediator m) =>
|
||||
Results.Ok(await m.Send(cmd)));
|
||||
}
|
||||
|
||||
public record CreateOrderCommand(string CustomerId, List<OrderItem> Items) : IRequest<OrderDto>;
|
||||
|
||||
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderDto>
|
||||
{
|
||||
readonly AppDbContext _db;
|
||||
public CreateOrderHandler(AppDbContext db) => _db = db;
|
||||
public async Task<OrderDto> Handle(CreateOrderCommand cmd, CancellationToken ct)
|
||||
{
|
||||
var order = Order.Create(cmd.CustomerId, cmd.Items);
|
||||
_db.Orders.Add(order);
|
||||
await _db.SaveChangesAsync(ct);
|
||||
return OrderDto.FromEntity(order);
|
||||
}
|
||||
}</pre>
|
||||
<p>That's it. One file contains the API endpoint, the command, the handler, and the result DTO. When you need to modify how orders are created, you open this file. When a new developer joins, they read this file and understand the entire flow. This is the power of VSA.</p>
|
||||
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul>
|
||||
<li>VSA organizes by <strong>business capability</strong>, not technical concern</li>
|
||||
<li>.NET 10 Minimal APIs + MediatR make VSA natural and concise</li>
|
||||
<li>Every Indotalent product uses this architecture — study real production code</li>
|
||||
<li>One file = one feature = zero confusion</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200 rounded-2xl">
|
||||
<p class="text-sm text-gray-700 font-semibold mb-2">Ready to study real VSA code?</p>
|
||||
<p class="text-sm text-gray-600 mb-4">Every Indotalent product is a complete .NET 10 application built with Vertical Slice Architecture. Complete source code — $21 each.</p>
|
||||
<a href="../index.html#products" class="btn-primary text-sm">Explore Products</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-6"><a href="index.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><div class="flex flex-wrap gap-4 text-sm"><a href="../index.html#products" class="text-gray-500 hover:text-brand-500">Products</a><a href="index.html" class="text-gray-500 hover:text-brand-500">Blog</a><a href="../support.html" class="text-gray-500 hover:text-brand-500">Support</a></div><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script>
|
||||
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Building Maintainable .NET Applications with MediatR and VSA | Indotalent Blog</title>
|
||||
<meta name="description" content="How MediatR enables CQRS within Vertical Slice Architecture. Pipeline behaviors for validation, logging, and transactions. Complete code examples." />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/vsa-mediatr.html" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"Article","headline":"Building Maintainable .NET Applications with MediatR and VSA","author":{"@type":"Person","name":"go2ismail"},"datePublished":"2026-05-01","publisher":{"@type":"Organization","name":"Indotalent"}}</script>
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{ "@type":"ListItem","position":1,"name":"Home","item":"https://www.indotalent.com/" },{ "@type":"ListItem","position":2,"name":"Blog","item":"https://www.indotalent.com/blog/" },{ "@type":"ListItem","position":3,"name":"MediatR and VSA","item":"https://www.indotalent.com/blog/vsa-mediatr.html" }]}</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 20px -4px rgba(249,115,22,0.45)}article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}article h3{font-size:1.15rem;font-weight:600;margin:1.5rem 0 0.5rem}article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}article pre{background:#1f2937;color:#f9fafb;padding:16px;border-radius:10px;overflow-x:auto;margin:1rem 0;font-size:0.85rem;line-height:1.6}article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100"><div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5"><ol class="flex items-center gap-1.5 text-xs text-gray-400"><li><a href="../index.html" class="hover:text-brand-500">Home</a></li><li><span class="text-gray-300">/</span></li><li><a href="index.html" class="hover:text-brand-500">Blog</a></li><li><span class="text-gray-300">/</span></li><li><span class="text-gray-600 font-medium">MediatR & VSA</span></li></ol></div></nav>
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div></nav>
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12"><div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">May 2026 · 9 min read</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">Building Maintainable .NET Applications with MediatR and VSA</h1>
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-8"><p class="text-sm text-green-800 font-semibold mb-1">TL;DR</p><p class="text-sm text-green-700">MediatR is the backbone of VSA in .NET. It implements the Mediator pattern with <code>IRequest<TResponse></code> and pipeline behaviors for cross-cutting concerns like validation, logging, and transactions.</p></div>
|
||||
<article>
|
||||
<p>MediatR is more than just a mediator library — it's the backbone of effective Vertical Slice Architecture in .NET. It implements the Mediator pattern with two key abstractions: <code>IRequest<TResponse></code> for commands/queries and <code>IRequestHandler<TRequest, TResponse></code> for their handlers.</p>
|
||||
<h2>Commands, Queries, and CQRS</h2>
|
||||
<p>In VSA, each feature slice exposes its API through MediatR. Commands (which mutate state) are separate from Queries (which read state). This is CQRS — Command Query Responsibility Segregation — and it maps naturally onto VSA. A <code>CreateOrderCommand</code> and its handler live in <code>Features/Orders/</code>. The handler uses EF Core to persist the order. A <code>GetOrderByIdQuery</code> also lives in the same folder, optimized for reads with EF Core's no-tracking queries.</p>
|
||||
<h2>Pipeline Behaviors: Cross-Cutting Concerns</h2>
|
||||
<p>MediatR's pipeline behaviors handle cross-cutting concerns without cluttering business logic. Logging, validation, and transaction management are implemented once as behaviors and applied to all handlers automatically:</p>
|
||||
<pre>public class ValidationBehavior<TRequest, TResponse>
|
||||
: IPipelineBehavior<TRequest, TResponse>
|
||||
{
|
||||
public async Task<TResponse> Handle(TRequest req,
|
||||
RequestHandlerDelegate<TResponse> next, CancellationToken ct)
|
||||
{
|
||||
var validator = _validators.GetValidator<TRequest>();
|
||||
if (validator != null) await validator.ValidateAsync(req, ct);
|
||||
return await next();
|
||||
}
|
||||
}</pre>
|
||||
<p>This keeps your handlers clean — they focus purely on business logic while behaviors handle the plumbing.</p>
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul><li>MediatR provides the CQRS backbone for VSA feature slices</li><li>Pipeline behaviors eliminate duplicated cross-cutting code</li><li>Indotalent products ship with pre-configured MediatR pipelines</li></ul>
|
||||
</article>
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200 rounded-2xl"><p class="text-sm text-gray-700 font-semibold mb-2">Explore VSA Products</p><p class="text-sm text-gray-600 mb-4">Every Indotalent product uses MediatR + VSA. Complete source code — $21 each.</p><a href="../index.html#products" class="btn-primary text-sm">Explore Products</a></div>
|
||||
<div class="mt-6"><a href="index.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div></section>
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="https://schema.org/Article">
|
||||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-G9G5HP50VK"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-G9G5HP50VK');</script>
|
||||
<meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vertical Slice Architecture vs Traditional Layered Architecture: Side-by-Side Code | Indotalent Blog</title>
|
||||
<meta name="description" content="7 files in 4 projects vs 1 file. A concrete code comparison of VSA and traditional layered architecture for the same 'Create Customer' feature." />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<link rel="canonical" href="https://www.indotalent.com/blog/vsa-vs-layered-architecture.html" />
|
||||
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"Article","headline":"Vertical Slice Architecture vs Traditional Layered Architecture: Side-by-Side Code","author":{"@type":"Person","name":"go2ismail"},"datePublished":"2026-02-01","publisher":{"@type":"Organization","name":"Indotalent"}}</script>
|
||||
<script type="application/ld+json">{ "@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{ "@type":"ListItem","position":1,"name":"Home","item":"https://www.indotalent.com/" },{ "@type":"ListItem","position":2,"name":"Blog","item":"https://www.indotalent.com/blog/" },{ "@type":"ListItem","position":3,"name":"VSA vs Layered Architecture","item":"https://www.indotalent.com/blog/vsa-vs-layered-architecture.html" }]}</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script><script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>tailwind.config={theme:{extend:{colors:{brand:{50:'#fff7ed',100:'#ffedd5',200:'#fed7aa',300:'#fdba74',400:'#fb923c',500:'#f97316',600:'#ea580c',700:'#c2410c'}},fontFamily:{sans:['Inter','system-ui','sans-serif']}}}}</script>
|
||||
<style>.nav-blur{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.gradient-text{background:linear-gradient(135deg,#f97316,#ea580c);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.btn-primary{display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f97316,#ea580c);color:#fff;font-weight:600;border-radius:12px;padding:12px 24px;transition:all 0.2s;box-shadow:0 4px 14px -4px rgba(249,115,22,0.35);text-decoration:none}article h2{font-size:1.5rem;font-weight:700;margin:1.75rem 0 0.75rem;color:#111827}article p{font-size:0.95rem;line-height:1.8;color:#4b5563;margin-bottom:1rem}article code{background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.85rem;color:#ea580c}article ul{list-style:disc;padding-left:1.5rem;margin-bottom:1rem}article ul li{font-size:0.95rem;line-height:1.8;color:#4b5563}</style>
|
||||
</head>
|
||||
<body class="bg-white text-gray-900 antialiased">
|
||||
<nav aria-label="Breadcrumb" class="pt-20 bg-white border-b border-gray-100"><div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5"><ol class="flex items-center gap-1.5 text-xs text-gray-400"><li><a href="../index.html" class="hover:text-brand-500">Home</a></li><li><span class="text-gray-300">/</span></li><li><a href="index.html" class="hover:text-brand-500">Blog</a></li><li><span class="text-gray-300">/</span></li><li><span class="text-gray-600 font-medium">VSA vs Layered</span></li></ol></div></nav>
|
||||
<nav class="fixed top-0 left-0 right-0 z-50 nav-blur bg-white/85 border-b border-gray-200/60"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-16"><a href="../index.html" class="flex items-center gap-2.5"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-8 w-auto" /></a><div class="hidden lg:flex items-center gap-6"><a href="../index.html#products" class="text-sm font-medium text-gray-600 hover:text-brand-500">Products</a><a href="../for-beginners.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Beginners</a><a href="../for-freelancers.html" class="text-sm font-medium text-gray-600 hover:text-brand-500">Freelancers</a><a href="index.html" class="text-sm font-medium text-brand-600">Blog</a></div></div></div></nav>
|
||||
<section class="pt-24 pb-10 lg:pt-32 lg:pb-12"><div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-2 mb-3"><span class="text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 rounded-full px-2.5 py-0.5">VSA</span><span class="text-xs text-gray-400">Feb 2026 · 8 min read</span></div>
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 mb-4">Vertical Slice Architecture vs Traditional Layered Architecture: Side-by-Side Code</h1>
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-8"><p class="text-sm text-green-800 font-semibold mb-1">TL;DR</p><p class="text-sm text-green-700">Traditional approach: 7 files across 4 projects for one feature. VSA: 1 file in 1 folder. Same functionality, zero cognitive overhead for VSA.</p></div>
|
||||
<article>
|
||||
<p>Let's compare how the same "Create Customer" feature looks in both architectures.</p>
|
||||
<h2>Traditional Layered Approach</h2>
|
||||
<p>Files you must touch across a typical Clean Architecture solution:</p>
|
||||
<ul><li><code>Controllers/CustomersController.cs</code> — HTTP endpoint</li><li><code>Application/Customers/CreateCustomerCommand.cs</code> — DTO</li><li><code>Application/Customers/CreateCustomerHandler.cs</code> — business logic</li><li><code>Application/Common/Interfaces/IAppDbContext.cs</code> — abstraction</li><li><code>Domain/Entities/Customer.cs</code> — domain entity</li><li><code>Infrastructure/Persistence/AppDbContext.cs</code> — EF Core context</li><li><code>Web/Program.cs</code> — DI registration</li></ul>
|
||||
<p>Seven files across four projects. When a bug is reported in customer creation, you must trace the flow through all of them.</p>
|
||||
<h2>VSA Approach</h2>
|
||||
<p>One file in one folder <code>Features/Customers/CreateCustomer.cs</code> contains: the Minimal API endpoint, the MediatR command, the handler with all business logic, the EF Core query, and the response DTO. When a bug is reported, you open one file. When you need to add a field to customer creation, you edit one file. This is the essence of VSA: <strong>maximum cohesion, minimum scattering</strong>.</p>
|
||||
<h2>Key Takeaways</h2>
|
||||
<ul><li>Layered: 7 files, 4 projects. VSA: 1 file, 1 folder.</li><li>Bug tracing: hours vs minutes</li><li>Feature addition: touch 7 files vs touch 1 file</li><li>All Indotalent products demonstrate this VSA pattern in production code</li></ul>
|
||||
</article>
|
||||
<div class="mt-10 p-6 bg-gradient-to-r from-orange-50 to-amber-50 border border-orange-200 rounded-2xl"><p class="text-sm text-gray-700 font-semibold mb-2">Compare with Indotalent VSA code</p><p class="text-sm text-gray-600 mb-4">Every product demonstrates VSA in a real, working application. $21 each.</p><a href="../index.html#products" class="btn-primary text-sm">Explore Products</a></div>
|
||||
<div class="mt-6"><a href="index.html" class="text-sm text-brand-600 hover:text-brand-700">← Back to Blog</a></div>
|
||||
</div></section>
|
||||
<footer class="bg-white border-t border-gray-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-4"><a href="../index.html" class="flex items-center gap-2"><img src="../img/logo-indotalent-dark.svg" alt="Indotalent" class="h-5 w-auto" /></a><p class="text-xs text-gray-500">© 2024-2026 Indotalent. <span class="text-brand-500">♥</span> by go2ismail</p></div></div></footer>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user