In-depth technical library

Practical .NET and software architecture guides

Go beyond the weekly newsletter with focused, long-form guides built around production decisions, working code, and the trade-offs that matter.

66 published guides

Explore the library

Browse The .NET Weekly archive →
  • Vertical Slice Architecture

    Combining Vertical Slices With CQRS in .NET

    CQRS and Vertical Slice Architecture are a natural pair. Commands and queries are already separate - putting each in its own slice makes them independent and easy to optimize.

    • clean-architecture
    • dotnet
    • vertical-slice-architecture
    Read the guide →
  • Vertical Slice Architecture

    Cross-Cutting Concerns in Vertical Slice Architecture

    One criticism of Vertical Slice Architecture is code duplication across slices. Here is how to handle cross-cutting concerns like validation, logging, and caching without breaking feature isolation.

    • dotnet
    • software-architecture
    • vertical-slice-architecture
    Read the guide →
  • Modular Monoliths

    Defining Module Boundaries With Bounded Contexts

    Draw module boundaries wrong and you fight your own architecture on every feature. Bounded contexts give you a systematic way to draw them: map business capabilities, give each module its own language and data, and validate with the change test. Plus the three boundary mistakes that sink most modular monoliths.

    • ddd
    • modular-monolith
    • software-architecture
    Read the guide →
  • Modular Monoliths

    Event-Driven Communication Between Modules in .NET

    The Ordering module publishes an event, and Shipping and Notifications react without the publisher knowing they exist. The catch: an in-process event bus runs every handler synchronously in the caller's scope, so a slow or failing consumer becomes the publisher's problem. Here is the full setup, and the point where the outbox pattern has to take over.

    • distributed-systems
    • dotnet
    • modular-monolith
    Read the guide →
  • Vertical Slice Architecture

    Feature Folders in .NET: Organizing Code by Feature

    Stop organizing code by technical concern (Controllers, Services, Models). Organize by feature instead - so everything related to placing an order lives in one folder. Here is how to implement feature folders in .NET.

    • dotnet
    • software-architecture
    • vertical-slice-architecture
    Read the guide →
  • Modular Monoliths

    How to Build a Modular Monolith in .NET Step by Step

    Build a working Modular Monolith skeleton in .NET: three modules, each with its own schema and DbContext, cross-module calls that go through Contracts projects only, and an in-process event bus. Architecture tests fail the build the moment someone crosses a boundary.

    • dotnet
    • modular-monolith
    • software-architecture
    Read the guide →
  • Modular Monoliths

    Saga Pattern in a Modular Monolith

    Placing an order touches Ordering, Inventory, Payment, and Shipping, and any step can fail after the previous ones already committed. The saga pattern breaks the process into local transactions with compensating actions, coordinated by an orchestrator that fits in one class. Here is how to build one inside a modular monolith, without a distributed transaction in sight.

    • distributed-systems
    • dotnet
    • modular-monolith
    Read the guide →
  • Modular Monoliths

    Schema-Per-Module vs Database-Per-Module: Which Data Isolation Strategy Should You Pick?

    Schema-per-module and database-per-module both enforce module boundaries at the data layer, but they differ sharply on transactions, operations, and your path to microservices. Here is the EF Core setup for both, the five trade-offs that actually matter, and the triggers that tell you when to graduate from schemas to separate databases.

    • dotnet
    • ef-core
    • modular-monolith
    Read the guide →
  • Modular Monoliths

    Strangler Fig Pattern for Modular Monolith Migration

    Big-bang rewrites fail for predictable reasons: they take longer than planned, the legacy system keeps changing underneath them, and the business sees nothing until the end. The Strangler Fig pattern replaces a legacy monolith one bounded context at a time, with feature-flag routing, data sync, and a parallel run before every cutover. The system stays fully functional throughout.

    • dotnet
    • modular-monolith
    • software-architecture
    Read the guide →
  • Vertical Slice Architecture

    Testing Vertical Slices in .NET

    Vertical slices change the shape of your tests. Instead of repository, service, and controller tests held together by mocks, you test one feature at a time: validators in microseconds, handlers in isolation, and the full slice from HTTP to database with WebApplicationFactory and Testcontainers.

    • dotnet
    • testing
    • vertical-slice-architecture
    Read the guide →
  • Vertical Slice Architecture

    Vertical Slice Architecture vs Clean Architecture

    Clean Architecture manages complexity through layer discipline. Vertical Slice Architecture manages it through feature isolation. Teams treat this as a religious war, but it is a fit question: rich shared domain logic points one way, features with wildly different complexity point the other. Here is how the two compare, and the hybrid most real projects land on.

    • clean-architecture
    • dotnet
    • software-architecture
    Read the guide →
  • Vertical Slice Architecture

    Vertical Slice Architecture With Carter in .NET

    Carter turns Minimal API endpoints into self-registering modules: each feature defines its routes, and app.MapCarter() wires them up at startup. Combined with Vertical Slice Architecture, every feature owns its endpoint, handler, and validator in one folder. Here is the full setup, from installation to integration tests.

    • aspnetcore
    • dotnet
    • vertical-slice-architecture
    Read the guide →
  • Vertical Slice Architecture

    When to Choose Vertical Slice Architecture Over Layered Architecture

    Four files across four folders for a query that returns one object: that is the layered tax on every feature. Sometimes the tax buys you real structure, and sometimes it buys you nothing. Here are the concrete signals that tell you when Vertical Slice Architecture is the better fit for your .NET project, and when layers still win.

    • dotnet
    • software-architecture
    • vertical-slice-architecture
    Read the guide →
  • Modular Monoliths

    When to Extract a Module Into a Microservice

    Extract a module too early and you lock unstable boundaries into network contracts. Wait too long and extraction becomes a multi-month rewrite. These are the four concrete drivers that justify extracting a module into a microservice, the readiness checklist to pass first, and the extraction process that keeps a rollback path open.

    • dotnet
    • microservices
    • modular-monolith
    Read the guide →
  • Clean Architecture

    Authentication and Authorization in Clean Architecture

    Authentication is infrastructure, authorization is a business rule, and confusing the two is how ASP.NET Core leaks into your domain. Here is where JWT validation, the ICurrentUserService abstraction, permission checks, and ownership rules each live, and how to keep them enforced beyond HTTP endpoints.

    • clean-architecture
    • dotnet
    • security
    Read the guide →
  • Clean Architecture

    Background Jobs in Clean Architecture

    Where do background jobs fit in Clean Architecture? Treat them as entry points, exactly like controllers. The job schedules and triggers, the application layer does the work. Here is the structure, the scoped-service wiring that trips everyone up, and complete examples with BackgroundService and Quartz.

    • aspnetcore
    • background-jobs
    • clean-architecture
    Read the guide →
  • Clean Architecture

    Clean Architecture Solution Template for .NET

    Setting up a Clean Architecture project from scratch takes time. Here is a complete .NET solution template with the right project structure, references, and configurations.

    • clean-architecture
    • dotnet
    • software-architecture
    Read the guide →
  • Clean Architecture

    Clean Architecture vs Onion Architecture vs Hexagonal Architecture

    Clean Architecture, Onion Architecture, and Hexagonal Architecture all solve the same fundamental problem: decoupling business logic from infrastructure. But they differ in structure, naming, and emphasis. Here is a practical comparison to help you choose.

    • clean-architecture
    • dotnet
    • software-architecture
    Read the guide →
  • Clean Architecture

    Clean Architecture With Minimal APIs in .NET

    Minimal API endpoints and Clean Architecture are a natural fit: the endpoint receives HTTP, dispatches a command, and maps the result back. No business logic, no controllers, no ceremony. Here is how to organize endpoints by feature, validate with endpoint filters, and return consistent Problem Details.

    • aspnetcore
    • clean-architecture
    • dotnet
    Read the guide →
  • Clean Architecture

    Dependency Rule in Clean Architecture Explained

    Source code dependencies must only point inward. That one sentence decides your project references, where your interfaces live, and why the DI container sits at the edge. Here is how the Dependency Rule works, how control can flow outward while dependencies point inward, and how to enforce it with the compiler and architecture tests.

    • clean-architecture
    • dotnet
    • software-architecture
    Read the guide →
  • Clean Architecture

    Domain Events vs Integration Events in .NET

    A domain event is handled in-process, inside the same transaction. An integration event crosses service boundaries through a message broker and needs the outbox pattern to survive a crash. Confusing the two leads to lost events and accidental coupling. Here is where the line sits, with implementation patterns for both sides.

    • clean-architecture
    • ddd
    • distributed-systems
    Read the guide →
  • Clean Architecture

    Exception Handling Strategy in Clean Architecture

    Expected failures are not exceptional, so stop throwing them. A clear strategy gives each layer one job: the domain throws on invariant violations, the application returns typed Results, and a single global handler maps everything else to Problem Details. Here is how the pieces fit together in a .NET Clean Architecture.

    • clean-architecture
    • dotnet
    • software-architecture
    Read the guide →
  • Clean Architecture

    How to Organize Use Cases in Clean Architecture

    A folder structure that works at 5 use cases falls apart at 200. Group by feature, keep one use case per folder, and name with verb-noun domain language, and your Application layer reads like a list of things the system can do. Here are the three layouts you will encounter, and why only one of them scales.

    • clean-architecture
    • dotnet
    • software-architecture
    Read the guide →
  • Clean Architecture

    Logging Strategy in Clean Architecture

    One pipeline behavior can log every use case with timing and failures, so handlers stay clean and the domain never sees an ILogger. Here is a layer-by-layer logging strategy for Clean Architecture: domain events instead of logs in the domain, behaviors in the application, direct logging in infrastructure, and middleware at the edge.

    • clean-architecture
    • dotnet
    • observability
    Read the guide →
  • Clean Architecture

    Mapping Between Layers in Clean Architecture

    Request to command, command to entity, entity to response: a single operation can pass through three mappings, and most of them are ceremony. EF Core projections eliminate the read-side mapping entirely, and simple extension methods handle the rest. Here is when mapping between layers earns its keep, and why you rarely need AutoMapper.

    • clean-architecture
    • design-patterns
    • dotnet
    Read the guide →
  • Clean Architecture

    The Application Layer in Clean Architecture

    The Application layer is where your use cases live: commands, queries, and the handlers that orchestrate your domain objects. A good handler reads like a table of contents (load, act, save). Here is how to structure the layer in .NET and keep business logic from leaking into it.

    • clean-architecture
    • cqrs
    • dotnet
    Read the guide →
  • Clean Architecture

    The Domain Layer in Clean Architecture

    Every business rule in your system should have exactly one home: the Domain layer. That means entities that guard their invariants, value objects instead of raw primitives, and domain events for side effects. Here is what belongs in the Domain layer, what does not, and how to keep it at zero external dependencies.

    • clean-architecture
    • ddd
    • dotnet
    Read the guide →
  • Clean Architecture

    The Infrastructure Layer in Clean Architecture

    Databases, message brokers, email providers, caches: everything that talks to the outside world lives in the Infrastructure layer. It implements the interfaces your inner layers define, and it is the only place EF Core should ever appear. Here is how to structure it in .NET, from repositories and the DbContext to a clean DI registration module.

    • clean-architecture
    • dotnet
    • ef-core
    Read the guide →
  • Clean Architecture

    When to Use Clean Architecture (And When Not To)

    A five-endpoint CRUD API with four projects, domain events, and a CQRS pipeline is over-engineering, not discipline. Clean Architecture pays off on complex domains, long-lived codebases, and multi-team projects. Here is a decision framework for spotting which one you have, and a migration path for when you guess wrong.

    • clean-architecture
    • dotnet
    • software-architecture
    Read the guide →
  • Clean Architecture

    Where Do Transactions Belong in Clean Architecture?

    The use case defines what must succeed or fail together, so the transaction boundary belongs to the application layer. Here are three ways to implement that in .NET, ranked: a single SaveChanges as the implicit boundary, a unit of work abstraction, and a transaction pipeline behavior.

    • clean-architecture
    • dotnet
    • ef-core
    Read the guide →
  • Clean Architecture

    Where Does Caching Belong in Clean Architecture?

    Caching is infrastructure, but the decision to cache is application-level. Get that split wrong and cache concerns leak into your use cases, or worse, into your domain. Here are two clean approaches: a decorator over your repositories and a pipeline behavior driven by the use case itself.

    • caching
    • clean-architecture
    • dotnet
    Read the guide →
  • Testing

    Architecture Fitness Functions in .NET With ArchUnitNET

    An architecture rule nobody enforces is a wish with a code-review lottery attached. A fitness function turns it into a build failure that names the offending type. Here is how to write them with ArchUnitNET, including slice cycle detection, the three ways a fitness function quietly lies to you, and what to do when your rule set outgrows code.

    • architecture
    • clean-architecture
    • dotnet
    Read the guide →
  • Caching

    Content-Addressed Caching in .NET: Cache Keys That Never Go Stale

    Cache invalidation is hard because we name cache entries after locations. Name them after a hash of their inputs instead, and the whole class of stale-read bugs stops being possible: change any input, get a different key, recompute under it. Git, Docker, and NuGet all run on this idea, and it works just as well inside your own application.

    • architecture
    • caching
    • dotnet
    Read the guide →
  • Clean Architecture

    Clean Architecture in .NET: The Complete Guide

    A practical map of Clean Architecture in .NET: dependency direction, layer responsibilities, use-case organization, cross-cutting concerns, and the tradeoffs that tell you when the structure is worth its cost.

    • clean-architecture
    • dotnet
    • software-architecture
    Read the guide →
  • Databases

    Distributed Locking With Postgres Advisory Locks in .NET

    You probably do not need Redis, RedLock, or ZooKeeper for distributed locking. If your instances already share a Postgres database, they share a lock manager too. Session-level advisory locks serialize work across every instance and auto-release the moment a holder dies, which is the exact property TTL-based leases spend fencing tokens trying to approximate.

    • database
    • distributed-systems
    • dotnet
    Read the guide →
  • Messaging

    How I Use NATS JetStream as a Job Queue in .NET

    My first job queue was a Redis list, and it had two ways to silently lose a job: a worker crash after LPOP, and a broker restart. NATS JetStream closed both with a work-queue stream, a durable pull consumer, and one ordering rule: publish the result before you ack the job. Here is the whole design, small enough to read in five minutes.

    • architecture
    • dotnet
    • messaging
    Read the guide →
  • Modular Monoliths

    Modular Monolith Architecture in .NET: The Complete Guide

    A modular monolith keeps one deployment while enforcing business boundaries inside the codebase. This guide connects module design, data isolation, communication, testing, and the evidence that can justify extracting a service later.

    • dotnet
    • modular-monolith
    • software-architecture
    Read the guide →
  • Observability

    OpenTelemetry Collectors: The Agent + Gateway Pattern

    One OpenTelemetry Collector is easy. The interesting design shows up the moment your apps run on more than one machine: a small agent collector on every box, forwarding to one gateway collector that fans out to Tempo, Loki, and Prometheus. Here is the two-tier setup I run in production, including the two config lines that stop the collector from OOM-killing the box it shares with real workloads.

    • devops
    • dotnet
    • observability
    Read the guide →
  • Databases

    Why Postgres Ignores Your Index (Sargability, Taught by a Kata)

    Your query is correct, your tests pass, and the index you carefully created is never touched. The usual culprit is one innocent-looking habit: wrapping the column in a function. Here is what sargability means, how to catch the problem with EXPLAIN, and how I built a coding kata that grades your query plan so the lesson actually sticks.

    • database
    • performance
    • postgresql
    Read the guide →
  • Performance

    Async/Await Performance Pitfalls in .NET

    Async improves throughput only while the work stays nonblocking. Sync-over-async, accidental serialization, unnecessary state machines, and unbounded fan-out can make an asynchronous endpoint slower or deadlock it entirely.

    • csharp
    • dotnet
    • performance
    Read the guide →
  • Domain-Driven Design

    Bounded Context in DDD Explained With Examples

    Bounded Contexts are the most important strategic pattern in Domain-Driven Design. They define explicit boundaries around domain models, giving the same word different meanings in different contexts. Here is a practical guide with .NET examples.

    • ddd
    • dotnet
    • modular-monolith
    Read the guide →
  • AI for .NET

    Building a RAG System in .NET

    Every RAG demo works until you point it at your own documents and it confidently makes things up. The model is rarely the problem. Chunking, embeddings, and retrieval quality decide whether the answer comes from your data or from thin air. Here is a complete RAG pipeline in .NET with Microsoft.Extensions.AI, PostgreSQL with pgvector, hybrid search, re-ranking, and source attribution.

    • ai
    • dotnet
    Read the guide →
  • API Design

    Choosing the Right HTTP Status Codes for Your API

    Nobody argues about 200 and 500. The fights are in the middle: 400 vs 422, 401 vs 403, 404 vs 410, and whether 200-with-an-error-body is ever acceptable (it is not). A decision tree for the ambiguous cases, with the ASP.NET Core mappings to make it stick.

    • api-design
    • aspnetcore
    • rest
    Read the guide →
  • Databases

    Dapper in .NET: A Practical Guide

    Dapper's value proposition is predictability: the SQL you write is the SQL that runs, with object mapping and nothing else. This guide covers the patterns you need in a real application: queries, parameters, multi-mapping, QueryMultiple, transactions, and where Dapper fits next to EF Core.

    • dapper
    • database
    • dotnet
    Read the guide →
  • EF Core

    EF Core vs Dapper: When to Use Each

    EF Core and Dapper are the two most popular .NET data access libraries. EF Core provides a full ORM with change tracking, migrations, and LINQ. Dapper gives you raw SQL performance with minimal abstraction. Here is when to use each one.

    • dotnet
    • ef-core
    • performance
    Read the guide →
  • Microservices

    Microservices in .NET: Getting Started Guide

    A .NET microservice system needs more than separately deployed APIs: each service owns its data, communication survives partial failure, and traces cross every boundary. This guide builds a small end-to-end system and makes the cost visible before you choose it.

    • dotnet
    • microservices
    • software-architecture
    Read the guide →
  • ASP.NET Core

    Minimal APIs in .NET: Complete Guide

    Minimal APIs are a lightweight way to build HTTP APIs in .NET without controllers, startup classes, or conventions. Here is a complete guide covering routing, validation, authentication, and structuring Minimal APIs at scale.

    • aspnetcore
    • dotnet
    Read the guide →
  • Modular Monoliths

    Modular Monolith vs Microservices: How to Choose

    Modular Monoliths give you most of microservices benefits - loose coupling, independent modules, clear boundaries - without the operational complexity. Here is a practical comparison to help you decide which fits your project.

    • dotnet
    • microservices
    • modular-monolith
    Read the guide →
  • Caching

    Multi-Level Caching in .NET With FusionCache

    Rolling your own IMemoryCache plus Redis gets you 80% of a caching stack and silently skips the three hard parts: cross-instance invalidation, cache stampede, and serving stale data when the source is down. FusionCache gives you L1, L2, a backplane, and all three behaviors out of the box.

    • caching
    • fusioncache
    • performance
    Read the guide →
  • Resilience

    Polly v8: Resilience Pipelines Explained

    Polly v8 replaced policies with resilience pipelines: a rewritten core with allocation-free execution, built-in telemetry, and one composition model instead of PolicyWrap. Here is how the new API maps to the old one, how to register pipelines with DI, and why the order you add strategies changes what your pipeline actually does.

    • aspnetcore
    • dotnet
    • polly
    Read the guide →
  • Messaging

    RabbitMQ vs Kafka for .NET Applications

    RabbitMQ deletes messages once consumed; Kafka keeps them and moves a cursor. That single design decision explains almost every difference between them: replay, fan-out, ordering, routing, and scaling. Understand it and the choice for your .NET system mostly makes itself.

    • kafka
    • messaging
    • rabbitmq
    Read the guide →
  • Distributed Systems

    Saga Pattern in .NET: Managing Distributed Transactions

    The Saga pattern manages distributed transactions across multiple services by breaking them into local transactions with compensating actions. Here is how to implement both orchestration and choreography sagas in .NET.

    • distributed-systems
    • dotnet
    • software-architecture
    Read the guide →
  • Vertical Slice Architecture

    The REPR Pattern in ASP.NET Core

    The REPR pattern (Request-Endpoint-Response) replaces bloated controllers with focused, single-purpose endpoints. Here is how to implement it in ASP.NET Core with Minimal APIs.

    • aspnetcore
    • clean-architecture
    • design-patterns
    Read the guide →
  • Testing

    Unit Testing Best Practices in .NET

    Unit tests should be fast, reliable, and maintainable. But poorly written tests become a burden that slows development. Here are the best practices for writing effective unit tests in .NET.

    • csharp
    • dotnet
    • testing
    Read the guide →