DEV Community

Sabin Sim
Sabin Sim

Posted on

C#.NET - day 07

day 07: Interface & Dependency Inversion

Decoupling services from concrete implementations using contracts

Introduction

Up to this point, the system worked well. However, the Service layer was still tightly coupled to a concrete Repository implementation.

This step introduces interfaces and dependency inversion to ensure that the Service does not depend on a specific storage mechanism.


🌊 System Flow

  1. Request: The Controller receives a request.
  2. Delegation: The Controller forwards the request to the Service.
  3. Dependency Inversion: The Service does not know the concrete repository. It only calls methods defined in IHelloRepository.
  4. Execution: Program.cs injects the actual implementation (HelloRepository) that fulfills the contract.

1️⃣ Model: Data Contract

The model defines the shape of the data exchanged across layers.

namespace HelloFlow.Models;

public class HelloResponse
{
    public string Message { get; set; } = string.Empty;
    public DateTime CreatedAt { get; set; }
    public string Location { get; set; } = string.Empty;
}
Enter fullscreen mode Exit fullscreen mode

2️⃣ Repository Contract: IHelloRepository

The interface acts as a contract. It defines what must be done, not how it is done.

using HelloFlow.Models;

namespace HelloFlow.Repositories;

public interface IHelloRepository
{
    void Save(HelloResponse data);
    List<HelloResponse> GetAll();
    List<HelloResponse> SearchAdvanced(string keyword, int pageNumber, int pageSize);
}
Enter fullscreen mode Exit fullscreen mode

Any repository implementation that follows this contract can be used interchangeably.


3️⃣ Repository Implementation

This class fulfills the contract using an in-memory, thread-safe data structure.

using System.Collections.Concurrent;
using HelloFlow.Models;

namespace HelloFlow.Repositories;

public class HelloRepository : IHelloRepository
{
    private readonly ConcurrentDictionary<string, HelloResponse> _storage = new();

    public void Save(HelloResponse data)
    {
        var key = Guid.NewGuid().ToString();
        _storage.TryAdd(key, data);
    }

    public List<HelloResponse> GetAll()
    {
        return _storage.Values.ToList();
    }

    public List<HelloResponse> SearchAdvanced(string keyword, int pageNumber, int pageSize)
    {
        var query = _storage.Values.AsEnumerable();

        if (!string.IsNullOrWhiteSpace(keyword))
        {
            query = query.Where(x =>
                x.Message.Contains(keyword, StringComparison.OrdinalIgnoreCase));
        }

        return query
            .OrderByDescending(x => x.CreatedAt)
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .ToList();
    }
}
Enter fullscreen mode Exit fullscreen mode

4️⃣ Service Layer: Depending on the Contract

The Service no longer depends on a concrete repository. It works with IHelloRepository, not HelloRepository.

using HelloFlow.Models;
using HelloFlow.Repositories;

namespace HelloFlow.Services;

public class HelloService
{
    private readonly IHelloRepository _repository;

    public HelloService(IHelloRepository repository)
    {
        _repository = repository;
    }

    public HelloResponse GetHello(string name)
    {
        var response = new HelloResponse
        {
            Message = $"Hello, {name}!",
            CreatedAt = DateTime.Now,
            Location = "Cazis, Switzerland"
        };

        _repository.Save(response);
        return response;
    }

    public List<HelloResponse> FindHelloAdvanced(string keyword, int page, int size)
    {
        int safePage = page < 1 ? 1 : page;
        int safeSize = size > 50 ? 50 : size;

        return _repository.SearchAdvanced(keyword, safePage, safeSize);
    }
}
Enter fullscreen mode Exit fullscreen mode

This change represents the core of dependency inversion: the Service depends on an abstraction, not a concrete implementation.


5️⃣ Controller: Request Handling Only

using Microsoft.AspNetCore.Mvc;
using HelloFlow.Services;

namespace HelloFlow.Controllers;

[ApiController]
[Route("api/[controller]")]
public class HelloController : ControllerBase
{
    private readonly HelloService _service;

    public HelloController(HelloService service)
    {
        _service = service;
    }

    [HttpGet]
    public IActionResult SayHello(string name)
    {
        var result = _service.GetHello(name);
        return Ok(result);
    }

    [HttpGet("search")]
    public IActionResult SearchHello(
        [FromQuery] string? name,
        [FromQuery] int page = 1,
        [FromQuery] int size = 10
    )
    {
        var results = _service.FindHelloAdvanced(name ?? "", page, size);
        return Ok(results);
    }
}
Enter fullscreen mode Exit fullscreen mode

6️⃣ Program.cs: Dependency Wiring

This is where the abstraction is connected to its concrete implementation.

using HelloFlow.Services;
using HelloFlow.Repositories;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// Interface → Implementation mapping
builder.Services.AddSingleton<IHelloRepository, HelloRepository>();
builder.Services.AddScoped<HelloService>();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.MapControllers();
app.Run();
Enter fullscreen mode Exit fullscreen mode

🧠 Why This Matters

By introducing an interface:

  • The Service does not care how data is stored
  • The Repository can be replaced without changing Service logic
  • Future database integration becomes trivial

Only the DI configuration changes when swapping implementations.


🧠 One-Sentence Summary

This step is not about writing more code, but about designing a system that can change without breaking.


✍️ My Notes & Reflections

  • Each step adds more complexity, but it also gives me a stronger sense of structure.
  • It feels as if I am slowly watching a factory come to life, understanding how its parts begin to move and connect with each other.

Top comments (0)