Showing posts with label Design Patterns. Show all posts
Showing posts with label Design Patterns. Show all posts

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.

Monday, November 17, 2025

Architecture - Unit of Work (UOW) Pattern

1️⃣ What is Unit of Work?

Unit of Work (UoW) is a design pattern that:

  • Tracks changes made during a business transaction
  • Ensures all operations either succeed together (Commit)
  • Or fail together (Rollback)
  • Avoids partial writes to the database
  • Works like a transaction manager for repositories

๐Ÿ“Œ Simple Definition

UoW ensures that multiple database operations inside a business use case are treated as one atomic unit — either fully saved or fully reverted.


 


2️⃣ Why Do We Need Unit of Work?

Problems Without UoW

  • Calling multiple repositories → each saves independently
  • Leads to partial updates, inconsistent data, and race conditions
  • No coordination between repositories

With UoW

  • All repository actions are batched
  • Saved only when Commit() is called
  • If error occurs → Rollback() ensures consistency

3️⃣ How UoW Works Internally

EF Core already implements UoW concepts:

  • DbContext tracks changes (UoW)
  • DbContext.SaveChanges() = Commit

But in DDD or layered architecture, we wrap DbContext inside a custom UoW interface.


4️⃣ Commit & Rollback Explained

๐Ÿ‘‰ Commit

  • Saves all tracked changes to database
  • Calls SaveChanges() or SaveChangesAsync()
  • Ensures all operations succeed

๐Ÿ‘‰ Rollback

  • Cancels the transaction
  • Discards all uncommitted changes
  • Database remains unchanged

5️⃣ Unit of Work Flow (Simple)

Start UoW ↓ Use Repositories ↓ Make Changes (Add/Update/Delete) ↓ Commit() → All changes are saved OR Rollback() → All changes discarded

6️⃣ UoW with EF Core — Code Example

IUnitOfWork.cs

public interface IUnitOfWork : IDisposable { IProductRepository Products { get; } IOrderRepository Orders { get; } Task<int> CommitAsync(); // Commit = Save void Rollback(); // Rollback = Discard changes }

UnitOfWork.cs

public class UnitOfWork : IUnitOfWork { private readonly AppDbContext _context; public UnitOfWork(AppDbContext context) { _context = context; Products = new ProductRepository(context); Orders = new OrderRepository(context); } public IProductRepository Products { get; private set; } public IOrderRepository Orders { get; private set; } public async Task<int> CommitAsync() { return await _context.SaveChangesAsync(); } public void Rollback() { // Discard changes tracked by DbContext foreach (var entry in _context.ChangeTracker.Entries()) { switch (entry.State) { case EntityState.Modified: case EntityState.Deleted: case EntityState.Added: entry.State = EntityState.Detached; break; } } } public void Dispose() { _context.Dispose(); } }

7️⃣ Using Unit of Work — Example

Example Business Use Case: PlaceOrder

public async Task PlaceOrder(Order order) { using var uow = _unitOfWork; try { uow.Orders.Add(order); uow.Products.UpdateStock(order.ProductId, -order.Quantity); await uow.CommitAsync(); // Commit all } catch (Exception) { uow.Rollback(); // Discard changes throw; } }

✔ Both repositories share the same DbContext

✔ UoW ensures either:

  • Order saved + Stock updated → together
  • Or nothing saved → Rollback

8️⃣ Interview-Friendly Summary

Repository

  • Provides an abstraction over data logic
  • Works per entity (ProductRepository, OrderRepository)

Generic Repository

  • Provides base CRUD logic for all entities
  • Reduces duplication

Unit of Work

  • Controls multiple repositories
  • One transaction for the whole business operation
  • Offers Commit() and Rollback()
  • Maintains consistency

Architecture - Repository & Generic Repository Patterns

 ✅ 1. Repository Pattern

What it is

A Repository acts as an abstraction layer between your domain/business logic and the data access layer.
Instead of writing EF Core queries directly in controllers/services, you wrap them inside repository classes.

Why we use it

  • Keeps business logic independent from persistence logic

  • Easy to mock for unit testing

  • Central place to maintain DB queries


Example: Product Repository (Non-Generic)

Entity

public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } }

IProductRepository

public interface IProductRepository { Task<Product> GetByIdAsync(int id); Task<IEnumerable<Product>> GetAllAsync(); Task AddAsync(Product product); void Update(Product product); void Delete(Product product); }

Implementation

public class ProductRepository : IProductRepository { private readonly AppDbContext _context; public ProductRepository(AppDbContext context) { _context = context; } public async Task<Product> GetByIdAsync(int id) => await _context.Products.FindAsync(id); public async Task<IEnumerable<Product>> GetAllAsync() => await _context.Products.ToListAsync(); public async Task AddAsync(Product product) => await _context.Products.AddAsync(product); public void Update(Product product) => _context.Products.Update(product); public void Delete(Product product) => _context.Products.Remove(product); }

๐Ÿ‘‰ This is a repository dedicated only to Product.


2. Generic Repository Pattern

What it is

A Generic Repository provides a reusable base class for CRUD operations.
Instead of writing a repository per entity, you write one that works with all entities.

Why we use it

  • Avoids duplicated CRUD code

  • Cleaner structure

  • Extensible for entity-specific repositories


IGenericRepository

public interface IGenericRepository<T> where T : class { Task<T> GetByIdAsync(int id); Task<IEnumerable<T>> GetAllAsync(); Task AddAsync(T entity); void Update(T entity); void Delete(T entity); }

Implementation

public class GenericRepository<T> : IGenericRepository<T> where T : class { protected readonly AppDbContext _context; public GenericRepository(AppDbContext context) { _context = context; } public async Task<T> GetByIdAsync(int id) => await _context.Set<T>().FindAsync(id); public async Task<IEnumerable<T>> GetAllAsync>() => await _context.Set<T>().ToListAsync(); public async Task AddAsync(T entity) => await _context.Set<T>().AddAsync(entity); public void Update(T entity) => _context.Set<T>().Update(entity); public void Delete(T entity) => _context.Set<T>().Remove(entity); }

๐Ÿ”ฅ Entity-Specific Repository + Generic Repository

Sometimes we extend generic repository for custom queries:

public interface IProductRepository : IGenericRepository<Product> { Task<IEnumerable<Product>> GetProductsAbovePrice(decimal price); }
public class ProductRepository : GenericRepository<Product>, IProductRepository { public ProductRepository(AppDbContext context) : base(context) { } public async Task<IEnumerable<Product>> GetProductsAbovePrice(decimal price) { return await _context.Products .Where(p => p.Price > price) .ToListAsync(); } }

Sunday, November 9, 2025

Architecture - CQRS (Command Query Responsibility Segregation)

 ๐Ÿงฉ 1. What is CQRS?

CQRS stands for:

Command
Query
Responsibility
Segregation

It means:

Separate the read (Query) and write (Command) operations of your application into different models.

 


Problem with traditional architectures and how CQRS pattern solves it-

Traditional architectures often face challenges in handling high loads and managing complex data requirements. In these systems, the same model is used for both reading (fetching data) and writing (updating data), which can lead to performance issues. As the application grows, handling large read and write requests together becomes harder, creating bottlenecks and slowing down responses.

CQRS solves this by separating read and write operations into distinct models.

  • This means write requests (commands) and read requests (queries) are processed independently, optimizing each for its specific task.
  • As a result, CQRS allows systems to handle higher traffic efficiently, improves performance, and simplifies scaling by allowing independent optimization of read and write parts.


How to Sync Databases with CQRS Design Pattern -
Synchronizing databases in a system that follows the CQRS pattern can be challenging due to the separation of the write and read sides of the application.

How-to-Sync-Databases-with-CQRS


Here is how you can handle the synchronization:

  1. Write to the Command Database: When you make changes (create, update, delete), they are first saved in the command database. This database is optimized for handling write operations.
  2. Generate Events: After the write operation is successful, the system generates events that describe what changed (like "Order Created" or "User Updated"). These events serve as notifications about the updates.
  3. Update the Query Database: The read database, optimized for fast queries, listens for these events and applies the changes to its own copy of the data. This way, the query database gets updated with the latest information.
  4. Eventual Consistency: The key idea is that the query database doesn’t have to update immediately. There can be a slight delay, but eventually, both databases will sync, ensuring consistency over time.


Challenges of using CQRS Design Pattern

Below are the challenges of using CQRS Design Pattern:
  • Complexity: Your system may become more complex if you use CQRS, particularly if you are unfamiliar with the pattern. It can be difficult to coordinate data synchronization, manage distinct read and write models, and guarantee consistency between the two.
  • Consistency: Maintaining consistency between the read and write models can be challenging, especially in distributed systems where data updates may not propagate immediately. Careful planning and execution are necessary to guarantee stability over time without compromising scalability or performance.
  • Data Synchronization: It might be difficult to keep the read and write models in sync, particularly when handling complicated data transformations or big data sets. It can be beneficial to use strategies like message queues or event sources.
  • Performance Overhead: Implementing CQRS can introduce performance overhead, especially if not done carefully. For example, using event sourcing for the write model can impact write performance, while keeping the read model updated in real-time can impact read performance.
  • Operational Complexity: Operational complexity may rise while managing two databases or data storage (one for read and one for write). This covers duties including monitoring, backup and restoration, and guaranteeing data durability and high availability.

Best Practices for implementing CQRS pattern

Below are some of the best practices for implementing CQRS pattern:

Separate Read and Write Models Carefully:
Clearly divide the system into models for reading data (queries) and writing data (commands). This separation helps keep each model simple and optimized for its specific task.

Use Asynchronous Communication When Needed:
Since commands and queries are separated, consider using asynchronous messaging for commands. This helps the system stay responsive and handle high traffic efficiently, even if some operations take longer.

Keep Commands and Queries as Simple as Possible:
Design commands to focus only on changing data (like “CreateOrder” or “UpdateUser”) and queries only on retrieving data (like “GetOrderDetails”). Avoid mixing read and write logic in either part to keep things clean and maintainable.

Embrace Event Sourcing for Data Consistency:
Event sourcing can be paired with CQRS to keep a record of all changes. Each change is saved as an event, and the current state is rebuilt from these events. This can make it easier to track history, recover data, or audit changes.

Consider the Complexity of Your System:
CQRS adds some complexity, so it’s best suited for systems with high read and write demands or complex business rules. For simpler systems, CQRS might be overkill and add unnecessary development overhead.


⚙️ Without CQRS (Traditional CRUD)

In a typical system, we use a single model (e.g., Product) for everything:

public class ProductController : Controller { private readonly AppDbContext _context; public ProductController(AppDbContext context) { _context = context; } public IActionResult Get(int id) => Ok(_context.Products.Find(id)); public IActionResult Create(Product product)     {
        _context.Add(product); _context.SaveChanges(); return Ok();
    } }

๐Ÿ”ธ Both read and write share the same entity (Product).
๐Ÿ”ธ Works fine for small systems, but becomes hard to optimize and scale.


⚙️ With CQRS

We split the system into two logical parts:

TypeResponsibilityTypical OperationExample
CommandChanges system state (writes)Create / Update / DeleteCreateOrder, UpdateUser
QueryReads data onlyRead / GetGetOrders, GetUserById

➡️ Each side can have its own model, own data access, even separate databases.


๐Ÿง  2. Why CQRS?

BenefitDescription
ScalabilityYou can scale reads and writes independently.
PerformanceReads can use denormalized or cached data for speed.
Simplicity per sideEach side (Command/Query) is simpler and more focused.
MaintainabilityEasier to evolve business logic (especially in DDD).
Event-driven integrationWorks great with Event Sourcing and Message Queues.

๐Ÿงฉ 3. CQRS in .NET Example

Using MediatR (popular CQRS library for .NET):

Command:

public record CreateOrderCommand(string ProductName, int Quantity) : IRequest<int>;

Command Handler:

public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, int> { private readonly AppDbContext _context; public CreateOrderHandler(AppDbContext context) => _context = context; public async Task<int> Handle(CreateOrderCommand request, CancellationToken cancellationToken) { var order = new Order { ProductName = request.ProductName, Quantity = request.Quantity }; _context.Orders.Add(order); await _context.SaveChangesAsync(); return order.Id; } }

Query:

public record GetOrderByIdQuery(int Id) : IRequest<Order>;

Query Handler:

public class GetOrderByIdHandler : IRequestHandler<GetOrderByIdQuery, Order> { private readonly AppDbContext _context; public GetOrderByIdHandler(AppDbContext context) => _context = context; public async Task<Order> Handle(GetOrderByIdQuery request, CancellationToken cancellationToken) { return await _context.Orders.FindAsync(request.Id); } }

Controller:

public class OrderController : ControllerBase { private readonly IMediator _mediator; public OrderController(IMediator mediator) => _mediator = mediator; [HttpPost] public async Task<IActionResult> Create(CreateOrderCommand command) => Ok(await _mediator.Send(command)); [HttpGet("{id}")] public async Task<IActionResult> Get(int id) => Ok(await _mediator.Send(new GetOrderByIdQuery(id))); }

๐Ÿงญ 4. CQRS + Event Sourcing (Advanced)

Often used together:

  • Command side emits Events (e.g., OrderCreatedEvent)
  • Query side listens to those events and updates read models (denormalized views or cache).

So data flows asynchronously, improving performance and scalability.


๐Ÿง  5. When to Use (and When Not)

Use CQRS WhenAvoid CQRS When
Complex domain logicSimple CRUD apps
High read/write loadSmall internal tools
Need for scalabilityShort-lived prototypes
Event-driven architectureTraditional monolith

๐Ÿ“Š 6. Summary

AspectCommand SideQuery Side
PurposeModify dataRead data
ModelRich domain modelDTOs / View Models
DBOLTP (normalized)Read DB / Cache
ExampleCreateOrderCommandGetOrdersQuery


Saturday, July 11, 2015

Memento Design Pattern

Memento pattern is a way to preserve internal state of an object without violating encapsulation.

It is a widely used behavioral pattern.

There are three section of this pattern

Originator:
This is the original object for that memento get created
Memento: This is the copy of original class to preserve the original state of originator object.
Care Taker: This object holds memento object to restore to originator at any point of time.


Requirement:
During changing of object state if something goes wrong, object can be reverted to original state.

Use:
Original state can be re-stored at any point of time during application running.

Problem Solved:
Generally when required people restore object to its original state from database. So by using this pattern no need to query database to restore object.


Sample Code:

    
/// <summary>
    /// Original class
    /// </summary>
    public class Origintor
    {
        private string name;
        private string mobile;
        private string eMail;

        public Origintor(string _name, string _mobile, string _email)
        {
            name = _name;
            mobile = _mobile;
            eMail = _email;
        }

        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        public string Mobile
        {
            get
            {
                return mobile;
            }
            set
            {
                mobile = value;
            }
        }

        public string Email
        {
            get
            {
                return eMail;
            }
            set
            {
                eMail = value;
            }
        }

        public Memento SaveMemento()
        {
            return new Memento(name, mobile, eMail);
        }

        public void RestoreMemento(Memento objMemento)
        {
            this.Name = objMemento.Name;
            this.Mobile = objMemento.Mobile;
            this.Email = objMemento.Email;
        }
    }

Builder Design Pattern

The intent of this pattern is to separate construction of an object from its representation, so the same construction process can create different representation.

Its a creational design pattern.
Separates presentation of an object from its construction process.

There are three sections of this pattern:
  • Builder: Builder is responsible for defining construction process of each individual part of product. Builder has these small construction processes in it. 
  • Director: Director calls concrete builder of product as per client requirement.
  • Product: Product is final product that have multiple forms depends on which builder created the product.

Requirement: Whenever we have to create same kind of products with something different representation, we need this pattern.

Use: We can create different products using same construction process.

Problem Solved:  For creating products having different representation, no need to create whole separate process for each.



Sample Code:


    public class ReportDirector
    {
        public ReportDirector()
        {
        }

        public Report CreateReport(ReportBuilder reportBuilder)
        {
            reportBuilder.SetReportType();
            reportBuilder.SetReportTitle();
            reportBuilder.SetReportHeader();
            reportBuilder.SetReportFooter();
            return reportBuilder.GetReport();       
        }

    }

Friday, July 10, 2015

Abstract Factory Pattern

Provides a way to encapsulate a group of individual factories those have common theme (related) without specifying their concrete classes.

  • Its an important creational pattern
  • Abstract factory is an extension on factory pattern


Requirement: If you have created many factories or looking to create, client code become complex and scattered to call many factories. In that case abstract factory provides a centralized & simple way to call different individual factories using abstract factory.
Client only knows about Abstract factory & calls only Abstract factory for all individual factories.

Use: Client is able to call different related concrete factories from a single point of contact.
Client doesn't know about concrete classes & concrete factories. Client is aware only with abstract factory & abstract products (Interfaces).

Problems Solved: New concrete factories with related theme can be added without updating client.







































Sample Code:


    public abstract class AbstractFactory
    {      
        /// <summary>
        /// Get Connection Factory
        /// </summary>
        /// <param name="connectionType">
        /// 1- Sql Connection
        /// 2- Oledb Connection
        /// </param>
        /// <returns></returns>
        public abstract Connection GetConnectionObject(int connectionType)
        {
           return (new ConnectionFactory()).GetConnectionObject(connectionType);
        }

        /// <summary>
        /// Get Command Factory
        /// </summary>
        /// <param name="commandType">
        /// 1- Sql Command
        /// 2- Oledb Command
        /// </param>
        /// <returns></returns>
        public abstract Command GetCommandObject(int commandType) 
        {
           return (new CommandFactory()).GetCommandObject(commandType);
        }
     }

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