﻿Topic 6: Dependency Injection in TypeScript (Constructor Injection, Interfaces for Contracts, Basic IoC)

## 1. Problem Statement

**Scenario: Modern Payment Processing System**

You’re building a payment processing platform that must:

-   Support different payment gateways (Stripe, PayPal, BankTransfer).
    
-   Allow easy swapping or upgrading of gateways (e.g., for new regions).
    
-   Enable testing with fake gateways (no real transactions).
    
-   Keep the payment logic focused and maintainable.
    

**The problem:**  
How do you provide the payment module with the right gateway, swap gateways easily, and test without real payments  **while keeping your payment logic decoupled and flexible**?

## 2. Learning Objectives

By the end of this lesson, you’ll be able to:

-   Understand what Dependency Injection (DI) and Inversion of Control (IoC) are.
    
-   Use constructor injection to supply dependencies.
    
-   Define interfaces as contracts for dependencies.
    
-   Build flexible, testable, and maintainable TypeScript systems using DI.
    
## 3. Concept Introduction with Analogy

## Analogy: Plug-and-Play Power Sockets

Imagine a payment terminal (like a card reader) in a store:

-   The terminal doesn’t care what kind of plug (gateway) is used; it just needs a compatible socket.
    
-   You can plug in a Stripe adapter, a PayPal adapter, or a test adapter for training.
    
-   If you upgrade to a faster plug, you don’t need to change the terminal-just swap the plug.
    

**Dependency Injection**  is like using a universal socket:  
You can swap adapters (dependencies) without changing the device (business logic).

## **What is Dependency Injection?**

-   **Dependency Injection (DI)**  is a design pattern where an object’s dependencies are provided (“injected”) from outside, rather than hardcoded inside the object.
    
-   **Inversion of Control (IoC):**  The control of creating and supplying dependencies is inverted-handled by an external entity, not the object itself.
    

## **Why Use DI?**

-   **Decoupling:**  The main logic doesn’t depend on specific implementations.
    
-   **Testability:**  You can inject mocks or fakes for testing.
    
-   **Flexibility:**  Swap implementations without changing business logic.
    
-   **Maintainability:**  Changes to dependencies don’t ripple through the codebase.
    

## **DI in TypeScript: How It Works**

-   **Interfaces**  define contracts for dependencies.
    
-   **Constructor injection**  is the most common DI method.
    
-   **IoC containers**  (advanced) can automate dependency resolution (not covered in depth here).

## 4. Step-by-Step Data Modeling & Code Walkthrough

**Define a Contract (Interface) for Payment Gateways**
Interfaces define the expected behavior without implementation details.
```typescript
interface PaymentGateway {
  processPayment(amount: number): Promise<boolean>;
}
```
This interface ensures any payment gateway class implements the  `processPayment`  method.

 **Implement Concrete Payment Gateways**
Each gateway implements the interface with its own logic.

```typescript
class StripeGateway implements PaymentGateway {
  async processPayment(amount: number): Promise<boolean> {
    console.log(`Processing payment of $${amount} via Stripe.`);
    // Simulate API call...
    return true;
  }
}

class PaypalGateway implements PaymentGateway {
  async processPayment(amount: number): Promise<boolean> {
    console.log(`Processing payment of $${amount} via PayPal.`);
    // Simulate API call...
    return true;
  }
}
```
**Create the Payment Processor Using Constructor Injection**
The payment processor receives the gateway via its constructor.
```typescript
class PaymentProcessor {
  constructor(private gateway: PaymentGateway) {}

  async pay(amount: number): Promise<void> {
    const success = await this.gateway.processPayment(amount);
    if (success) {
      console.log("Payment successful!");
    } else {
      console.log("Payment failed.");
    }
  }
}
```

-   The processor  **does not create**  the gateway; it receives it.
    
-   This allows any class implementing  `PaymentGateway`  to be used.
    

**Using Different Gateways**
```typescript
const stripeGateway = new StripeGateway();
const paypalGateway = new PaypalGateway();

const processor1 = new PaymentProcessor(stripeGateway);
processor1.pay(100); // Uses Stripe

const processor2 = new PaymentProcessor(paypalGateway);
processor2.pay(200); // Uses PayPal
```
**Testing with Mock Gateways**

For testing, inject a mock gateway that simulates payment without real transactions.
```typescript
class MockGateway implements PaymentGateway {
  async processPayment(amount: number): Promise<boolean> {
    console.log(`Mock processing payment of $${amount}.`);
    return true;
  }
}

const mockGateway = new MockGateway();
const testProcessor = new PaymentProcessor(mockGateway);
testProcessor.pay(50); // Uses mock gateway for testing
```

## 5. Challenge 

-   Implement a new gateway class  `BankTransferGateway`  that logs payment processing.
    
-   Use it with  `PaymentProcessor`  to process a payment.
    
-   Write a mock gateway that simulates failure (`return false`) and test error handling.
## 6. Quick Recap & Key Takeaways

-   **Dependency Injection**  means supplying dependencies from outside, not creating them inside.
    
-   **Constructor Injection**  is the most common DI method in TypeScript.
    
-   **Interfaces**  define contracts that enable swapping implementations.
    
-   DI improves  **flexibility**,  **testability**, and  **maintainability**.
    

## 7. (Optional) Programmer’s Workflow Checklist

-   Identify dependencies your class needs.
    
-   Define interfaces for those dependencies.
    
-   Inject dependencies via constructors.
    
-   For testing, inject mocks or stubs.
    
-   Avoid creating dependencies inside business logic classes.

## 8. Coming up

**You’ve mastered Dependency Injection basics!**  
Next, explore  **IoC Containers**  (like InversifyJS) for automatic dependency management and advanced DI features.


