Thursday, October 9, 2025

🧩 Understanding Adapter Design Pattern in Software Development


πŸ” Introduction

In modern software architecture, systems often need to integrate with third-party libraries, legacy code, or APIs that don’t match our application's interface. The Adapter Design Pattern is a structural design pattern that bridges this gap by converting one interface into another that the client expects.

Simply put, it acts as a translator between incompatible interfaces — allowing two systems to work together smoothly without modifying their existing code.


πŸ’‘ Real-Time Example: Power Plug Adapter

Imagine you travel from India (which uses a 3-pin plug) to the US (which uses a 2-pin socket). You can’t directly plug your Indian charger into the US socket — you need an adapter.

Similarly, in software:

  • Client → Your laptop charger (expects 3-pin socket).

  • Service / API → The US socket (2-pin).

  • Adapter → A converter that connects both.

This is exactly what the Adapter Pattern does in programming — it adapts one interface to another.


🧠 Technical Explanation

The Adapter Design Pattern involves three main components:

  1. Target Interface – The interface expected by the client.

  2. Adaptee – The existing class with a different interface.

  3. Adapter – A class that bridges the Adaptee with the Target interface.


πŸ’» Example in C#

// Target Interface public interface INewPaymentGateway { void MakePayment(string account, double amount); } // Adaptee (Old system) public class OldPaymentSystem { public void ProcessTransaction(string customerCode, double value) { Console.WriteLine($"Payment of {value} processed for {customerCode} in Old System."); } } // Adapter Class public class PaymentAdapter : INewPaymentGateway { private readonly OldPaymentSystem _oldPaymentSystem; public PaymentAdapter(OldPaymentSystem oldPaymentSystem) { _oldPaymentSystem = oldPaymentSystem; } public void MakePayment(string account, double amount) { // Translate the request _oldPaymentSystem.ProcessTransaction(account, amount); } } // Client class Program { static void Main() { INewPaymentGateway payment = new PaymentAdapter(new OldPaymentSystem()); payment.MakePayment("USER123", 2500); } }

Output:

Payment of 2500 processed for USER123 in Old System.

✅ The PaymentAdapter bridges the old payment system (OldPaymentSystem) with the new interface (INewPaymentGateway).


πŸš€ Importance of Adapter Design Pattern

  • Integration Friendly: Easily integrate legacy systems with new applications.

  • Loose Coupling: Promotes flexibility by decoupling client and adaptee.

  • Reusability: Reuse existing incompatible code without rewriting it.

  • Scalability: Makes systems scalable for future interface changes.


🧩 Real-Time Use Cases

  • Connecting different payment gateways (PayPal, Stripe, Razorpay).

  • Integrating legacy APIs in new microservices.

  • Adapting different database connectors or logging frameworks.

  • In cloud or Azure services, when bridging old APIs to new versions.


🏁 Conclusion

The Adapter Design Pattern is a vital bridge for compatibility in software integration. It allows modern systems to interact with legacy code, third-party APIs, and incompatible interfaces — without rewriting the entire system. This ensures flexibility, maintainability, and reusability, key principles of robust software architecture.

Notification Flow in Microservices

 In a microservices architecture, services are decoupled. So if you want to notify a user about an event (like "order processed"), you usually use an asynchronous messaging system rather than calling services directly.

Flow Overview

  1. Event happens in a microservice
    Example: OrderService processes an order.

    var orderProcessedEvent = new OrderProcessedEvent { OrderId = 123, UserId = 456 };
  2. Publish Event to Message Broker

    • Microservice publishes the event to Azure Service Bus topic/queue.

    • Topics allow multiple subscribers, queues deliver to one consumer.

  3. Notification Service Subscribes

    • A dedicated Notification Microservice subscribes to the topic/queue.

    • When a message arrives, it triggers notification logic (email, SMS, push).

  4. Send Notification to Users

    • Notification Service decides which users should get notified.

    • It uses user preferences/configurations stored in a database.

  5. Delivery

    • Email: via SMTP, SendGrid, or Azure Communication Services.

    • Push: via Firebase or Azure Notification Hub.

    • SMS: via Twilio, Azure Communication Services, etc.


2. Azure Service Bus Setup

  • Queues: point-to-point (one consumer).

  • Topics & Subscriptions: publish-subscribe pattern (multiple consumers can get the same event).

Example:

  • OrderProcessedTopic

    • Subscription 1 → NotificationService

    • Subscription 2 → AnalyticsService

// Publishing var client = new ServiceBusClient(connectionString); var sender = client.CreateSender("OrderProcessedTopic"); await sender.SendMessageAsync(new ServiceBusMessage(JsonSerializer.Serialize(orderProcessedEvent)));
// Subscribing in Notification Service var processor = client.CreateProcessor("OrderProcessedTopic", "NotificationSubscription"); processor.ProcessMessageAsync += async args => { var body = args.Message.Body.ToString(); var orderEvent = JsonSerializer.Deserialize<OrderProcessedEvent>(body); // Send notification logic await SendEmailNotification(orderEvent.UserId, "Order Processed"); };

3. Email Notification Service

Components:

  1. Email Templates – HTML/Plain text templates.

  2. SMTP/Email Provider Configuration – SMTP settings (host, port, username, password) or a service like SendGrid/Azure Communication Services.

  3. Email Sending Logic – Called by the Notification Service when a message is received.

Sample code using SMTP:

public async Task SendEmailNotification(int userId, string message) { var user = await dbContext.Users.FindAsync(userId); if (user == null || string.IsNullOrEmpty(user.Email)) return; var smtp = new SmtpClient("smtp.yourmail.com") { Port = 587, Credentials = new NetworkCredential("username", "password"), EnableSsl = true, }; var mail = new MailMessage("noreply@yourapp.com", user.Email, "Order Update", message); await smtp.SendMailAsync(mail); }

4. Configuring Users for Notifications

  1. User Preferences Table

    • Table: UserNotifications

    • Columns: UserId, NotificationType, IsEnabled, Email, PushToken, PhoneNumber

    UserId | NotificationType | IsEnabled | Email | PushToken | PhoneNumber 456 | OrderProcessed | 1 | user@example.com | xyz | 9999999999
  2. Check Before Sending

    • Before sending, check if the user wants that type of notification:

    var config = dbContext.UserNotifications .Where(u => u.UserId == userId && u.NotificationType == "OrderProcessed" && u.IsEnabled) .FirstOrDefault(); if(config != null) SendEmail(...);
  3. Optional UI – Let users manage preferences in Settings → Notification Preferences.


5. Overall Flow Diagram

[OrderService] --(event)--> [Azure Service Bus Topic] --> [NotificationService] |--> Email/SMS/Push |--> Uses User Preferences from DB

Key Points:

  • Use Service Bus for decoupling services.

  • Use Notification Service as a central microservice.

  • Use user preferences to decide who gets notified.

  • Email provider can be SMTP, SendGrid, or Azure Communication Services.

  • Optional: extend for SMS, push notifications, or mobile apps.

Don't Copy

Protected by Copyscape Online Plagiarism Checker

Pages