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.
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
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.
Check cache
Request hits Redis first. A hit returns in microseconds — the database never notices.
Miss → query
On a miss, the service queries SQL Server through the normal repository path.
Populate
The result is written to Redis with a TTL suited to how fast the data changes.
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 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 scaled07 · 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.
Authenticate
User submits credentials; the auth service verifies them against hashed passwords.
Issue token
A signed JWT is returned carrying identity and role claims with an expiry.
Authorize requests
Every API call carries the token; middleware validates signature and expiry.
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.