Friday, November 21, 2025

Architecture - gRPC (General Remote Procedure Call) Framework

gRPC is a high-performance, open-source, RPC (Remote Procedure Call) framework developed by Google.

It allows services to communicate directly with each other using strongly typed contracts (.proto files) over a fast binary protocol (HTTP/2).

In simple terms:

gRPC allows one service to call a method in another service as if it were calling a local function, but over the network.


 


๐Ÿงฑ Key Components of gRPC

ComponentPurpose
Protocol Buffers (Protobuf)Language-neutral binary serialization format
.proto fileDefines contracts (messages + service methods)
gRPC client & server stubsAuto-generated code based on .proto
HTTP/2Transport protocol (multiplexing, streaming)

⚡ Why is gRPC so Fast?

๐Ÿš€ Because it uses:

  1. Binary protocol (Protocol Buffers) → smaller & faster than JSON
  2. HTTP/2 → multiplexing, header compression
  3. Streaming built-in
  4. Strongly typed contracts

Result:

  1. Super low latency
  2. High throughput
  3. Efficient for microservices

๐ŸŸข gRPC vs REST

FeaturegRPCREST
ProtocolHTTP/2HTTP/1.1
Data FormatProtobuf (binary)JSON (text)
SpeedFasterSlower
ContractStrong, auto-generatedOptional (Swagger)
StreamingFull support (4 types)Limited
Browser supportWeak (needs gRPC-Web)Native

✳️ When to Use gRPC

Use gRPC when you need:
✔ High performance
✔ Service-to-service communication
✔ Real-time communication
✔ Low latency mobile/IoT calls
✔ Strong contract enforcement

Not ideal for:
❌ Public APIs
❌ Browser-to-server calls (requires gRPC-web wrapper)


๐ŸŽฏ Types of gRPC Calls

TypeDescription
UnarySingle request → single response
Server streamingClient sends 1 request → server streams responses
Client streamingClient streams requests → server sends 1 response
Bi-directional streamingBoth sides stream simultaneously

๐Ÿ“„ Example .proto File

syntax = "proto3"; service OrderService { rpc GetOrder (OrderRequest) returns (OrderResponse); rpc StreamOrders (OrderRequest) returns (stream OrderResponse); } message OrderRequest { int32 orderId = 1; } message OrderResponse { int32 orderId = 1; string status = 2; }

๐Ÿงฉ C# Server Implementation

public class OrderServiceImpl : OrderService.OrderServiceBase { public override Task<OrderResponse> GetOrder(OrderRequest request, ServerCallContext context) { return Task.FromResult(new OrderResponse { OrderId = request.OrderId, Status = "Processing" }); } }

๐Ÿงฉ C# Client Implementation

var channel = GrpcChannel.ForAddress("https://localhost:5001"); var client = new OrderService.OrderServiceClient(channel); var response = await client.GetOrderAsync(new OrderRequest { OrderId = 1 }); Console.WriteLine(response.Status);

๐Ÿงฒ Where gRPC is Used (Real World)

  1. Google’s internal microservices
  2. Netflix → high-performance service communication
  3. Uber → low-latency RPC
  4. Dropbox
  5. Cloud Native (Kubernetes uses gRPC internally)

๐Ÿ–ฅ In .NET Ecosystem

.NET has first-class gRPC support, including:

  1. gRPC hosting via ASP.NET Core
  2. gRPC client library
  3. gRPC-Web
  4. Protobuf serialization
  5. Interceptors (similar to middleware)


๐Ÿง  Simple Interview-Perfect Definition

gRPC is a high-performance RPC framework using HTTP/2 and Protocol Buffers that enables fast, efficient communication between microservices with strongly typed contracts and support for streaming.


Thursday, November 20, 2025

Architecture - Saga Design Pattern

"Saga" is a distributed transaction pattern used in microservices to ensure data consistency without using 2-phase commit or distributed locks.

✔ In simpler words:

Saga ensures that a long-running business process composed of multiple microservice operations either completes successfully OR compensates (undoes) the already completed steps.


 

๐Ÿš€ Use Saga when:

  • A single business operation touches multiple microservices, each with its own DB.
  • You cannot use distributed transactions (like ACID/2PC).
  • You need an eventual consistency model.

๐ŸŽฏ Why Saga Is Needed

Typical distributed system problems:

  • Every service owns its database → no cross-service transaction possible.
  • Operations may fail in the middle.
  • You must prevent partial success (e.g., payment done but inventory not reserved).

Traditional ACID transactions do not work across services—Saga solves this using compensating actions.


๐Ÿง  Saga Pattern Key Principles

  1. A business workflow is broken into multiple local transactions.
  2. Each local transaction:
    • Performs a step.
    • Publishes an event.
  3. If any step fails → Saga triggers compensation (undo logic).

๐Ÿ”„ Two Types of Saga: Orchestration vs Choreography


๐Ÿ…ฐ️ 1. Choreography-Based Saga (Event-Driven)

Services talk to each other using events.


✔ Flow example:

  1. Order Service → "OrderCreated" event
  2. Inventory Service → reserves stock → "StockReserved"
  3. Payment Service → charges customer → "PaymentSuccess"
  4. Shipping Service → ships product → "OrderShipped"

If failure:

  • Payment fails → publish "PaymentFailed"
  • Inventory Service → receives and releases reserved stock
  • Order Service → marks order as cancelled

✔ Pros

  • No central coordinator
  • Simple for small flows
  • Very loosely coupled

❌ Cons

  • Hard to visualize / debug
  • Complex chains of events → “distributed spaghetti
  • Hard to evolve as workflow grows

๐Ÿ…ฑ️ 2. Orchestration-Based Saga

A central Saga Orchestrator commands each step.


✔ Flow example:

  1. Orchestrator → ReserveInventory()
  2. Orchestrator → ChargePayment()
  3. Orchestrator → CreateShipment()

If failure:

  • Orchestrator calls CompensateInventory(), RefundPayment(), etc.

✔ Pros

  • Centralized logic → easier to understand
  • Traceable
  • Easier for complex workflows

❌ Cons

  • Orchestrator becomes the “brain” → slightly more coupling


๐Ÿ“ฆ Real Example: E-Commerce Order Workflow

Step-by-step:

  1. Order Service
    • Creates order
    • Saga starts
  2. Inventory Service
    • Reserves stock
  3. Payment Service
    • Charges customer
  4. Shipping Service
    • Schedules delivery
  5. Saga completes

If Payment fails:

Saga automatically:

  • Cancels shipment
  • Releases stock
  • Cancels order

๐Ÿงฐ C# Example — Orchestration Saga Using MassTransit

public class OrderState : SagaStateMachineInstance { public Guid CorrelationId { get; set; } public string CurrentState { get; set; } public Guid OrderId { get; set; } } public class OrderStateMachine : MassTransitStateMachine<OrderState> { public State InventoryReserved { get; private set; } public Event<OrderCreated> OrderCreatedEvent { get; private set; } public OrderStateMachine() { InstanceState(x => x.CurrentState); Event(() => OrderCreatedEvent, x => x.CorrelateById(context => context.Message.OrderId)); Initially( When(OrderCreatedEvent) .Then(context => { Console.WriteLine("Order Created → Reserving inventory..."); }) .Publish(context => new ReserveInventory { OrderId = context.Instance.OrderId }) .TransitionTo(InventoryReserved) ); // Additional states + transitions omitted for brevity } }

This orchestrates:

  • Reserve inventory
  • Process payment
  • Ship order
  • Handle compensation

๐Ÿงฐ C# Example — Choreography Saga (Event-Driven)

Order Service publishes event:

await _publishEndpoint.Publish(new OrderCreated(orderId));

Inventory handles event:

public class OrderCreatedHandler : IConsumer<OrderCreated> { public async Task Consume(ConsumeContext<OrderCreated> context) { // Local transaction ReserveStock(context.Message.OrderId); await context.Publish(new StockReserved { OrderId = context.Message.OrderId }); } }

Payment handles event:

public class StockReservedHandler : IConsumer<StockReserved> { public async Task Consume(ConsumeContext<StockReserved> context) { var result = Charge(context.Message.OrderId); if (result) await context.Publish(new PaymentCompleted { OrderId = context.Message.OrderId }); else await context.Publish(new PaymentFailed { OrderId = context.Message.OrderId }); } }

๐Ÿงจ Compensating Transaction Example

public class PaymentFailedHandler : IConsumer<PaymentFailed> { public async Task Consume(ConsumeContext<PaymentFailed> context) { await context.Publish(new ReleaseStock { OrderId = context.Message.OrderId }); await context.Publish(new CancelOrder { OrderId = context.Message.OrderId }); } }

๐ŸŽฏ When to Use Saga

Use Saga when:

  • Business processes affect multiple services.
  • Rollbacks must be handled safely.
  • Workflow is long-running (minutes → hours).
  • Consistency between services is required.

Don't use Saga when:

  • Single service owns all data.
  • Strong ACID guarantees needed.
  • Steps cannot be compensated.

Wednesday, November 19, 2025

CI/CD - Canary Deployment

Below is a clear and practical explanation of how traffic is gradually shifted (“canaried”) in three different environments:



1. EC2-Based Environment (Classic servers / Auto Scaling Groups)

How Canary Traffic Shifting Works

In EC2-based deployments, canary rollout is typically done at the Load Balancer + Auto Scaling Group (ASG) level.

Step-by-Step Traffic Shift

  1. Two ASGs are created

    • ASG1 → Stable version (v1)
    • ASG2 → Canary version (v2)

  2. Both ASGs are registered under the same ALB Target Group or separate ALB Target Groups.

  3. Weighted Target Groups (ALB Feature)
    Example:

    • v1 TG weight = 90

    • v2 TG weight = 10

  4. Gradual shift is done by modifying weights

    • 90/10 → initial canary

    • 70/30 → monitor logs/errors

    • 30/70

    • 0/100 → success

  5. If issues appear:

    • instantly push traffic back to v1 by changing weights to 100/0.

Tools commonly used

  • AWS ALB Weighted Target Groups

  • AWS CodeDeploy (Canary Deployment for EC2/On-Prem)

  • ASG + Launch Templates


2. AWS Lambda-Based Environment

Canary deployments are native to Lambda—no infrastructure needed.

How Traffic Shifting Works

Lambda uses Aliases and Versions.

Example:

  • Version 1 → Stable

  • Version 2 → New release

  • Alias “LIVE” points to version 1 initially

Traffic Shift Steps

  1. Deploy new Lambda → version 2.

  2. Set alias traffic weights:

    LIVE alias: v1 = 90% v2 = 10%
  3. Let the system run and monitor logs (CloudWatch).

  4. Shift more traffic:

    • v1=70% / v2=30%

    • v1=30% / v2=70%

  5. Final cutover:

    • v1=0% / v2=100%

Rollback

Just update the alias back to:

  • v1=100%, v2=0%

Tools

  • AWS Lambda Aliases + Versions

  • AWS SAM / CDK for deployment

  • AWS CodeDeploy (Lambda Canary support)


3. Container-Based Environment (ECS / EKS / Kubernetes)

Traffic shifting is done at the service mesh / ingress layer.


3A. ECS (EC2 or Fargate)

Traffic Shift Method

  • Use two ECS services:

    • service-v1 (old)

    • service-v2 (new)

  • Attach services to two ALB Target Groups.

  • ALB Weighted Target Groups control traffic:

    • 90/10 → 70/30 → 50/50 → 0/100

Tools

  • ALB Weighted Target Groups

  • ECS Deployment Controller (Blue/Green via CodeDeploy)


3B. EKS / Kubernetes (most common)

How Canary Traffic Shift Works

Canary is controlled by:

  • Ingress Controller (NGINX / ALB Ingress)

  • Service Mesh (Istio / Linkerd / Consul)

Traffic Shift Example (Istio VirtualService)

spec: http: - route: - destination: host: myapp subset: v1 weight: 90 - destination: host: myapp subset: v2 weight: 10

Shift pattern

  • 90/10 → Initial Canary

  • 70/30 → Monitoring metrics

  • 30/70

  • 0/100 → Rollout complete

Rollback

Instantly revert weights to:

  • v1=100, v2=0

Tools

  • Istio / Linkerd / Consul Mesh

  • Argo Rollouts (best tool for progressive delivery)

  • Kubernetes Ingress with canary annotations


๐ŸŽฏ Summary Table

EnvironmentMechanismTraffic Shift MethodTools
EC2ASGs + ALBWeighted Target groups (10% → 100%)ALB, CodeDeploy
LambdaVersions + AliasAlias weight routingCodeDeploy, SAM, CDK
Containers (ECS)Services + ALBWeighted target groupsALB, ECS B/G deploy
EKS / K8sIngress / MeshWeighted routing rulesIstio, Argo Rollouts, NGINX Ingress

CI/CD - Blue-Green Vs Canary Deployments

 ๐ŸŸฆ Blue-Green Deployment

Concept

Blue-Green deployment means you maintain two identical production environments:

  • Blue = Current Production
  • Green = New Version

Only one is active at a time.

✔ How it works

  1. Deploy new application version to the Green environment.

  2. Test it thoroughly (Smoke tests, automation, health checks).

  3. Switch the traffic from Blue → Green (load balancer flip).

  4. If something goes wrong:

    • Switch traffic back to Blue (instant rollback).


๐ŸŸข Diagram (Simple Flow)



⭐ Advantages

BenefitDescription
Zero downtime deploymentUsers don't see any interruption.
Instant rollbackOne flip back to Blue.
Full environment testingGreen can be validated before traffic shift.

⚠️ Disadvantages

IssueExplanation
CostlyTwo full environments required.
Data sync complexityDB schema changes require careful planning.

๐Ÿ“Œ When to Use Blue-Green

  • Releases require zero downtime.
  • System is stable, and deployments are less frequent.
  • Company can afford 2 parallel production environments.


๐ŸŸง Canary Deployment

Concept

Canary deployment gradually releases the new version to a small subset of users first.

Like in coal mines, a "canary" tests the environment for problems.

✔ How it works

  1. Deploy new version v2 to a small percentage (e.g., 1% of users).
  2. Monitor behaviors (errors, CPU, logging, metrics).
  3. Gradually increase traffic:
    • 1% → 5% → 25% → 50% → 100%
  4. If issues appear:
    • Stop rollout
    • Rollback to previous version

๐ŸŸก Diagram (Traffic Split)



⭐ Advantages

BenefitDescription
Very safeOnly a small portion sees new version first.
Real user testingValidates with live traffic.
Fine-grained rollout controlYou can stop at any time.

⚠️ Disadvantages

IssueDescription
More complexRequires intelligent traffic routing & monitoring.
Harder to implementNeeds advanced infra (Istio, Envoy, Feature Flags).
Multiple versions live at onceMust manage compatibility.

๐Ÿ“Œ When to Use Canary Deployment

  • High-traffic applications.
  • Releases must be very cautious (e.g., banking/fintech).
  • Microservices environment.
  • Strong observability is in place (Prometheus, Grafana, ELK, AWS CloudWatch).


๐Ÿ†š Blue-Green vs Canary — Quick Comparison

FeatureBlue-GreenCanary
RollbackInstantGradual
ComplexityLowHigh
CostHigh (2 environments)Moderate
Risk LevelMediumVery Low
Traffic ControlAll users at onceSmall → full rollout
Use CaseEnterprise apps, stable releasesMicroservices, critical apps

๐ŸŸฉ Real-World Examples (Cloud & Kubernetes)


☁️ AWS Example

Blue-Green:

Using AWS Elastic Beanstalk, ALB switch, or CodeDeploy (blue/green).

Canary:

Using API Gateway Canary Release or App Mesh.


☸️ Kubernetes Example

Blue-Green:

Deploy new version as deployment-green, switch service selector.

Canary:

Using:

  • Istio VirtualService
  • Linkerd
  • Argo Rollouts
  • Flagger

Traffic can be:

  • 5% to canary
  • 95% to stable

๐Ÿง  Interview-Ready Summary

๐ŸŸฆ Blue-Green Deployment

➡ Two production environments.
➡ Switch traffic when new version is ready.
➡ Zero downtime + instant rollback.
➡ Easy but expensive.

๐ŸŸง Canary Deployment

➡ Slowly roll new version to small % of users.
➡ Real user feedback before full rollout.
➡ Harder but safest for high-risk releases.

EF - Compiled Queries

Do We Really Need Compiled Queries Today?

EF Core already auto-caches most queries.

Compiled queries are needed only for ultra-high performance APIs like:

  • Billing engines
  • High-traffic read endpoints
  • Microservices hitting DB thousands of times per minute

For normal applications—CRUD apps, admin portals—EF’s auto-caching is enough.




When Should You Use Compiled Queries?

Use compiled queries when:

✔ A query is executed very frequently
✔ The query is parameterized (e.g., by ID, Name)
✔ You want to avoid repeated LINQ-to-SQL compilation cost
✔ High-traffic microservices / Web APIs

Not needed for:

✘ Ad-hoc queries
✘ Complex queries built dynamically
✘ Queries that run only occasionally


๐ŸŸฆ EF Core Compiled Query Example (Recommended)

EF Core provides EF.CompileQuery static API.


Example 1: Simple compiled query

using Microsoft.EntityFrameworkCore; public static class CompiledQueries { public static readonly Func<AppDbContext, int, Task<Customer?>> GetCustomerById = EF.CompileAsyncQuery( (AppDbContext context, int id) => context.Customers.FirstOrDefault(c => c.Id == id) ); }

Use the compiled query

var customer = await CompiledQueries.GetCustomerById(dbContext, 10);


๐ŸŸง Compiled Query for Non-Async

EF also provides synchronous version: csharp Copy code public static readonly Func<AppDbContext, int, Customer?> GetCustomerSync = EF.CompileQuery( (AppDbContext db, int id) => db.Customers.FirstOrDefault(c => c.Id == id) );

๐Ÿ”ฅ Performance Benefit

EF Core normally does this per execution:

  1. Parse LINQ
  2. Build expression tree
  3. Translate to SQL
  4. Cache translation
  5. Execute

Compiled queries skip steps 1–3 and reuse the plan.

Expect 5%–30% speed improvement for repeated queries.


Summary

TopicExplanation
WhatPre-compiled LINQ-to-SQL for speed
WhyFaster execution for repeated queries
HowEF.CompileAsyncQuery()
WhenHigh-frequency, high-load operations
LimitationsNo dynamic query shapes

Tuesday, November 18, 2025

Architecture - Some Important Concepts of DDD

1. Domain Events

Domain events model things that happen inside the domain and allow other parts of the system to react.

Use cases

  • Send email after order placed
  • Reduce inventory
  • Publish message to message bus
  • Update read models

Domain Event Example

public class OrderPlacedEvent : IDomainEvent { public Guid OrderId { get; } public DateTime PlacedAt { get; } public OrderPlacedEvent(Guid orderId) { OrderId = orderId; PlacedAt = DateTime.UtcNow; } }

Aggregate raising event

public class Order : AggregateRoot { public List<OrderItem> Items { get; private set; } public void Place() { // business logic... AddDomainEvent(new OrderPlacedEvent(Id)); } }

Domain Event Handler

public class OrderPlacedHandler : IHandler<OrderPlacedEvent> { public Task Handle(OrderPlacedEvent domainEvent) { // Example action Console.WriteLine($"Order {domainEvent.OrderId} placed!"); return Task.CompletedTask; } }

2. Ubiquitous Language

It means:

Everyone (developers, business, testers, PMs) uses the SAME terms with SAME meaning.

Example:
Business says Order → OrderItem → Payment → Customer
So your code should also use Order, not “Tbl_Order”.

Bad example:

  • Business says “Order”

  • Code says “PurchaseTransactionRecord”

Good example:

public class Order { } public class OrderItem { }

This maintains clarity and reduces misunderstandings.


3. Bounded Context

A bounded context defines a logical boundary within which a domain model is consistent.

Example:

In an E-commerce system:

Bounded ContextMeaning of “Order”
Ordering BCOrder = items customer wants
Billing BCOrder = financial transaction
Shipping BCOrder = package to be shipped

Each BC has its own:

  • Model
  • Database
  • Services
  • Ubiquitous language

C# Example

Different BCs have different Order definitions:

Ordering BC

public class Order { public Guid Id { get; } public List<OrderItem> Items { get; } }

Shipping BC

public class OrderShipment { public Guid OrderId { get; } public string TrackingNumber { get; private set; } }

4. DTO (Data Transfer Object)

DTOs are simple objects used to transfer data across boundaries (API layer ↔ Application layer).

Not used for business logic.

Example

public class OrderDto { public Guid Id { get; set; } public decimal TotalAmount { get; set; } }

Controller returning DTO instead of domain entity

[HttpGet("{id}")] public async Task<ActionResult<OrderDto>> GetOrder(Guid id) { var order = await _orderService.GetOrder(id); return new OrderDto { Id = order.Id, TotalAmount = order.TotalAmount }; }

5. Aggregate

Aggregate = cluster of domain objects treated as a single unit
Aggregate Root = main entry point, enforces invariants.

Example

Order (root) → contains OrderItems (child entities)

public class Order : AggregateRoot { private readonly List<OrderItem> _items = new(); public IReadOnlyCollection<OrderItem> Items => _items.AsReadOnly(); public void AddItem(Guid productId, int qty, decimal price) { _items.Add(new OrderItem(productId, qty, price)); } }

Child entity:

public class OrderItem { public Guid ProductId { get; } public int Quantity { get; private set; } public decimal Price { get; private set; } internal OrderItem(Guid productId, int qty, decimal price) { ProductId = productId; Quantity = qty; Price = price; } }

Rules:

  • Only aggregate root is loaded from repository.
  • Only aggregate root is saved.
  • External code cannot modify inner entities directly.


6. Value Object

Value objects:

  • Immutable

  • Compared by values (not identity)

  • No Id

  • Represent concepts like Money, Email, Address, Quantity

C# Example

public class Email : ValueObject { public string Value { get; } public Email(string email) { if (!email.Contains("@")) throw new Exception("Invalid email"); Value = email; } protected override IEnumerable<object> GetEqualityComponents() { yield return Value; } }

Usage:

var userEmail = new Email("test@gmail.com");

7. Rich Model vs Anemic Model

Anemic Model (Anti-pattern)

  • Entities only have properties
  • Logic sits outside in service classes
  • Looks like a database table
  • Easy but poor domain modeling

Example:

public class Order { public Guid Id { get; set; } public decimal Total { get; set; } }

Business logic in services:

public decimal CalculateTotal(Order order) { return order.Items.Sum(i => i.Price); }

Rich Domain Model (Recommended in DDD)

Business logic lives inside domain entities.

Example:

public class Order { private readonly List<OrderItem> _items = new(); public void AddItem(Guid productId, int qty, decimal price) { if (qty <= 0) throw new Exception("Qty must be > 0"); _items.Add(new OrderItem(productId, qty, price)); } public decimal GetTotal() => _items.Sum(i => i.Price * i.Quantity); }

Rich model:

  • Enforces business rules
  • Reduces duplication
  • Maintains invariants


๐ŸŽฏ Summary Table

ConceptOne-line definition
RepositoryAbstraction over data access for aggregates
Domain EventsThings that happened in the domain
Ubiquitous LanguageSame terms used across business & code
Bounded ContextClear boundary where a domain model is consistent
DTOData-only object for transferring data
AggregateCluster of domain objects with rules
Value ObjectImmutable concept compared by value
Rich ModelEntities with behavior (preferred in DDD)
Anemic ModelEntities with only properties (anti-pattern)




CI/CD - Safe DB Changes/Migrations

Safe DB Migrations means updating your database schema without breaking the running application and without downtime . In real systems (A...