Isaac.

Utilize Dependency Injection (DI) for Loose Coupling

Table of Contents

  • What is Dependency Injection?
  • Why Loose Coupling Matters
  • Benefits of Using DI
  • Examples in Different Frameworks
    • ASP.NET
    • Spring Boot
    • Express.js
    • Next.js
    • Flask
    • Laravel
  • Conclusion

What is Dependency Injection?

Dependency Injection (DI) is a design pattern that removes the responsibility of creating dependencies from a class and hands it over to an external system. Instead of a class creating its own objects, it receives them from the outside.

Why Loose Coupling Matters

Loose coupling makes code flexible, easier to test, and maintainable. Without DI, classes become tightly coupled and harder to extend or refactor.

Benefits of Using DI

  • Improved testability
  • Reusability of components
  • Cleaner, more maintainable code
  • Flexibility in swapping implementations

Examples in Different Frameworks

ASP.NET

In ASP.NET, services are registered in the Program.cs file using builder.Services. The framework automatically injects the required service into endpoints or controllers.


// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Registering services
builder.Services.AddScoped<IMessageService, EmailMessageService>();

var app = builder.Build();

app.MapGet("/send", (IMessageService service) => {
    return service.Send("Hello from DI");
});

app.Run();

public interface IMessageService {
    string Send(string message);
}

public class EmailMessageService : IMessageService {
    public string Send(string message) => $"Email sent: {message}";
}
          

Spring Boot

Spring Boot uses annotations like @Service and@Autowired to declare beans and inject them automatically into dependent classes.


@Service
public class EmailService implements MessageService {
    public String send(String message) {
        return "Email sent: " + message;
    }
}

@RestController
public class MessageController {
    private final MessageService service;

    @Autowired
    public MessageController(MessageService service) {
        this.service = service;
    }

    @GetMapping("/send")
    public String sendMessage() {
        return service.send("Hello from DI");
    }
}
          

Express.js

Express doesn’t have DI built-in, but you can mimic DI by separating services into modules and importing them where needed.


// messageService.js
class MessageService {
  send(message) {
    return `Message sent: ${message}`;
  }
}

module.exports = new MessageService();

// server.js
const express = require("express");
const service = require("./messageService");
const app = express();

app.get("/send", (req, res) => {
  res.send(service.send("Hello from DI"));
});

app.listen(3000);
          

Next.js

Next.js relies on modular imports. You can define services in separate files and inject them into API routes or components as needed.


// services/messageService.ts
export class MessageService {
  send(message: string) {
    return `Message sent: ${message}`;
  }
}

// pages/api/send.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { MessageService } from "../../services/messageService";

const service = new MessageService();

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  res.status(200).json({ result: service.send("Hello from DI") });
}
          

Flask

Flask also doesn’t have built-in DI, but you can inject dependencies by creating service classes and passing them into routes.


from flask import Flask

class MessageService:
    def send(self, message):
        return f"Message sent: {message}"

service = MessageService()
app = Flask(__name__)

@app.route("/send")
def send():
    return service.send("Hello from DI")

if __name__ == "__main__":
    app.run()
          

Laravel

Laravel provides a powerful service container. You bind classes in service providers, and Laravel automatically resolves them into controllers or routes.


// app/Services/MessageService.php
namespace AppServices;

class MessageService {
    public function send($message) {
        return "Message sent: " . $message;
    }
}

// app/Providers/AppServiceProvider.php
public function register() {
    $this->app->singleton(AppServicesMessageService::class, function ($app) {
        return new AppServicesMessageService();
    });
}

// routes/web.php
use AppServicesMessageService;

Route::get('/send', function (MessageService $service) {
    return $service->send("Hello from DI");
});
          

Conclusion

Dependency Injection is a universal concept supported across many frameworks. It helps developers write modular, testable, and flexible code. By adopting DI, you achieve loose coupling and make your applications easier to scale and maintain.