Friday, January 30, 2026

AWS - Private link, VPC Interface Endpoint, VPC Gateway Endpoint, Direct Connect & VPC Peering

Private link, VPC Interface Endpoint, VPC Gateway Endpoint, Direct Connect & VPC Peering





1️⃣ VPC Endpoint (Big Picture)

A VPC Endpoint lets your VPC privately access AWS services without using the Internet, NAT Gateway, or IGW.

There are two types:

  • 🧩 Interface Endpoint
  • 🚪 Gateway Endpoint

👉 AWS PrivateLink is the technology behind Interface Endpoints.


2️⃣ Interface Endpoint (Powered by AWS PrivateLink)

🔹 What it is

  • Creates Elastic Network Interfaces (ENIs) in your subnet
  • You access services using private IPs
  • Uses AWS PrivateLink

🔹 Supported services

  • Most AWS services: SSM, EC2 API, CloudWatch, ECR, Secrets Manager
  • Your own services (via NLB)
  • SaaS / third-party services

🔹 Key points

  • Works across VPCs and accounts
  • Uses Security Groups
  • Charged per hour + per GB

🎨 Diagram

🟦 VPC A (Consumer) ┌──────────────────────────┐ │ EC2 🖥️ │ │ │ HTTPS (443) │ │ ▼ │ │ 🧩 Interface Endpoint │ ← ENI + Private IP │ │ │ └────┼─────────────────────┘ │ AWS PrivateLink 🔒 ▼ ┌──────────────────────────┐ │ 🟩 AWS Service / NLB │ │ (SSM / ECR / SaaS) │ └──────────────────────────┘

🧠 When to use

✅ Private access to AWS services
✅ Expose your service to other VPCs safely
✅ No CIDR overlap issues


3️⃣ Gateway Endpoint

🔹 What it is

  • Adds routes in your route table
  • No ENIs, no Security Groups
  • Only supports:

    • 🪣 S3
    • 📦 DynamoDB
🔹 Key points

  • Free
  • Scales automatically
  • Traffic stays inside AWS network

🎨 Diagram

🟦 VPC ┌────────────────────────────┐ │ EC2 🖥️ │ │ │ │ │ ▼ Route Table │ │ 🚪 Gateway Endpoint │ │ │ │ └───┼────────────────────────┘ ▼ 🪣 Amazon S3 📦 DynamoDB

🧠 When to use

✅ Access S3/DynamoDB privately
✅ Cheapest & simplest option


4️⃣ AWS PrivateLink (Concept)

🔹 What it really is

PrivateLink = Secure service exposure using Interface Endpoints

Think of it as:

“I want to expose my service privately without opening my VPC.”

🎨 Diagram

🟦 VPC A (Client) 🟪 VPC B (Provider) ┌─────────────┐ ┌────────────────┐ │ EC2 │ │ NLB │ │ │ │ │ + Service 🧩 │ │ ▼ │ └──────▲─────────┘ │ Interface │ │ │ Endpoint 🧩 │──PrivateLink───┘ └─────────────┘

🧠 When to use

✅ SaaS-like architecture
✅ Cross-account secure access
✅ No VPC peering / no CIDR worries


5️⃣ VPC Peering

🔹 What it is

  • 1-to-1 private connection between two VPCs
  • Uses private IPs
  • No transitive routing

🔹 Key points

  • Simple, but doesn’t scale well
  • CIDR must not overlap
  • No Security Group referencing across VPCs

🎨 Diagram

🟦 VPC A 🟨 VPC B ┌──────────┐ ┌──────────┐ │ EC2 🖥️ │────────────│ EC2 🖥️ │ └──────────┘ Peering 🔗 └──────────┘

🧠 When to use

✅ Small number of VPCs
✅ Full network-level access


6️⃣ AWS Direct Connect

🔹 What it is

  • Dedicated physical connection
  • From on-premises → AWS
  • Bypasses public internet

🔹 Key points

  • Very low latency
  • High bandwidth (1–100 Gbps)
  • Costly but stable

🎨 Diagram

🏢 On-Prem Data Center ┌───────────────────┐ │ Servers 🖥️ │ └─────────┬─────────┘ │ Dedicated Fiber 🔵 ▼ ┌────────────────────────┐ │ AWS Direct Connect ⚡ │ └─────────┬──────────────┘ ▼ 🟦 AWS VPC (Private Subnets)

🧠 When to use

✅ Hybrid cloud
✅ Predictable latency & bandwidth
✅ Enterprise workloads


7️⃣ One-Look Comparison Table
FeatureInterface EndpointGateway EndpointPrivateLinkVPC PeeringDirect Connect
Internet needed
ENI used
Route table change
Security Groups
Cross-account⚠️
CIDR overlap OK
Typical useAWS servicesS3/DDBSaaS / APIsVPC-to-VPCOn-prem

8️⃣ Simple Memory Trick 🧠
  • 🧩 Interface Endpoint“ENI + Private IP”
  • 🚪 Gateway Endpoint“Route table only”
  • 🔒 PrivateLink“Secure service publishing”
  • 🔗 VPC Peering“Flat VPC connection”
  • Direct Connect“Private fiber to AWS”

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...