Skip to main content

Architecture

Architecture & engineering

How I design scalable .NET systems — monolith vs microservices, clear APIs, messaging, SQL Server, AWS when it helps, and auth that sticks.

01 · Trade-offs

Monolith vs. microservices

Neither is a default. A well-layered monolith is the right call more often than conference talks admit — the skill is knowing where the line is.

Layered monolith

Single Deployable

UI + API + domain in one process

Layered Internals

Controllers → Services → Repositories

One Database

Shared schema, ACID transactions

  • + Simple deployment, debugging, and local dev
  • + Real ACID transactions across the whole domain
  • − Scales as one unit; teams and releases couple over time

Microservices

API Gateway

Single entry point

Service A

Own domain

Service B

Own domain

DB A

Owned data

DB B

Owned data

  • + Independent scaling, deployment, and team ownership
  • + Failure isolation between domains
  • − Eventual consistency, distributed debugging, infra cost

02 · REST APIs

Anatomy of a request

Every request in a well-built ASP.NET Core API passes through the same disciplined pipeline. Each layer has one job — and that separation is what keeps a codebase testable.

HTTP · request / response

GET /api/v1/patients/1042

Authorization: Bearer eyJhbGciOi…

HTTP/1.1 200 OK

Content-Type: application/json

{ "id": 1042, "name": "…", "visits": [ … ] }

Angular Client

HTTP request with JWT bearer token

Middleware Pipeline

Auth, CORS, rate limiting, logging

Controller

Model binding, validation, routing

Service Layer

Business rules, transaction boundaries

EF Core / Repository

LINQ queries, change tracking

SQL Server

Constraints, indexes, execution plans

03 · Microservices

Reference architecture

Gateway-fronted services with owned databases, asynchronous messaging, and a distributed cache — the shape of the systems I design toward.

Angular Client

SPA with JWT bearer tokens

API Gateway

Routing · rate limiting · TLS termination

Auth Service

Issues & validates JWTs

User Service

Profiles & identity data

Order Service

Business domain logic

RabbitMQ

Domain events between services

SQL Server

Database per service

Redis

Distributed cache

Workers

Async event consumers

Runs onDockerKubernetes

Order Service

Commits state, publishes OrderPlaced event

RabbitMQ Exchange

Routes by topic to bound queues

Queues

Durable, per-consumer buffering

Background Workers

Idempotent consumers process events

SQL Server / Redis

Update projections, evict stale cache

04 · Messaging

Event-driven design with RabbitMQ

Synchronous calls are for questions; events are for facts. When state changes, a service publishes a domain event and moves on — consumers react on their own schedule.

  • Services stay decoupled — publishers never know their consumers.
  • Durable queues absorb traffic spikes and consumer downtime.
  • Consumers are idempotent, so redelivery is safe by design.

05 · Caching

Caching with Redis

The cache-aside pattern: check the cache, fall back to the database, populate on the way out — and invalidate through the same events that keep services in sync.

  1. Check cache

    Request hits Redis first. A hit returns in microseconds — the database never notices.

  2. Miss → query

    On a miss, the service queries SQL Server through the normal repository path.

  3. Populate

    The result is written to Redis with a TTL suited to how fast the data changes.

  4. Invalidate

    Domain events evict affected keys, so caches never serve stale writes.

06 · Operations

Docker & Kubernetes

Containers make builds reproducible; orchestration makes them resilient. Each service ships as an image, and Kubernetes handles scheduling, scaling, and self-healing.

  • One image runs identically in dev, CI, and production.
  • Deployments declare desired state; the cluster converges to it.
  • Horizontal scaling per service — not per system.
kubectl — cluster
kubectl get pods -n platformNAME                        READY   STATUSapi-gateway-7d4b9c-x2lp     1/1     Runningauth-service-5f6d88-9qwm    1/1     Runningorder-service-6c7f91-k3nd   1/1     Runningrabbitmq-0                  1/1     Runningredis-master-0              1/1     Runningkubectl scale deploy order-service --replicas=3deployment.apps/order-service scaled

07 · Data

SQL Server engineering

The database is where correctness lives. These are the disciplines I apply on every schema I design.

Indexing strategy

Clustered indexes define physical order; non-clustered and covering indexes serve real query patterns. Every index is a write cost — added deliberately, verified with execution plans.

Transactions & isolation

Multi-step operations run in explicit transactions. Isolation levels are chosen per workload — consistency where money moves, throughput where dashboards read.

Stored procedures

Reporting-heavy and hot paths live close to the data. Parameterized procedures give predictable plans and a stable contract for complex queries.

Normalization first

Schemas start normalized to protect integrity; denormalization is a measured optimization with a reason attached, never a starting point.

08 · Security

Authentication & authorization

JWT-based identity flowing through every layer — validated at the gateway, enforced per endpoint, and applied again at the data level.

  1. Authenticate

    User submits credentials; the auth service verifies them against hashed passwords.

  2. Issue token

    A signed JWT is returned carrying identity and role claims with an expiry.

  3. Authorize requests

    Every API call carries the token; middleware validates signature and expiry.

  4. Enforce policy

    Role- and policy-based rules gate each endpoint — and queries filter data again.

09 · FAQ

Architecture questions

Short answers to the decisions people ask about most often.

  • When do you choose a monolith vs microservices?

    I start with a well-layered ASP.NET Core monolith when the domain is still forming or the team is small. Microservices make sense when bounded contexts are stable, teams can own deployables independently, and async boundaries (queues, events) reduce coupling more than they add operational cost.

  • When is RabbitMQ the right tool?

    RabbitMQ fits workflows that should not block an HTTP request — notifications, billing side effects, projections, and cross-service orchestration. I prefer durable queues, clear routing, and idempotent consumers so retries stay safe.

  • What role does Redis play in your designs?

    Redis is a cache and fast lookup layer for hot reads, session-like data, and rate-limit counters — not a replacement for SQL Server. Cached values always have an expiry or invalidation path tied to write operations.

  • How do you approach authentication in ASP.NET Core APIs?

    JWT bearer tokens with short lifetimes, role or policy-based authorization on endpoints, HTTPS everywhere, and a second check at the data layer so authorization is not only a controller concern.