Day 04: Introducing the Repository Pattern
Separating responsibilities to build systems that scale
Introduction
The core idea of this step is perfect separation of roles. Instead of putting everything into one place, we clearly divide responsibilities so that each part of the system focuses on a single concern.
This step may feel subtle, but once it clicks, many architectural decisions in C# and enterprise systems start to make sense.
The Structural Upgrade
Before:
- Service handled both business logic and data storage
- Controller called the Service directly
Pro Version:
- Repository → responsible only for storage
- Service → responsible only for business logic
- Controller → responsible only for request handling
In short:
Controller → Service → Repository
🌊 Flow: Upgraded System Behavior
- Controller receives the user’s name and forwards it to the Service.
- Service creates the business card (business logic) and tells the Repository to store it.
- Repository stores the data without asking questions.
1️⃣ Step 1: Hiring a Storekeeper (Repository)
First, we create a dedicated component that is responsible only for data storage. It does not know why data exists or how it is used.
Create a new folder named Repositories, then add a class called HelloRepository.
using HelloFlow.Models;
namespace HelloFlow.Repositories;
public class HelloRepository
{
private readonly List<HelloResponse> _storage = new();
public void Save(HelloResponse data)
{
_storage.Add(data);
}
public List<HelloResponse> GetAll()
{
return _storage;
}
}
This class does exactly two things:
- Save data
- Return stored data
No logic. No interpretation. Just storage.
2️⃣ Step 2: Reducing the Chef’s Duties (Service)
The Service no longer holds data directly. Instead, it delegates all storage-related work to the Repository.
Open Services/HelloService.cs and update it as follows:
using HelloFlow.Models;
using HelloFlow.Repositories;
namespace HelloFlow.Services;
public class HelloService
{
private readonly HelloRepository _repository;
public HelloService(HelloRepository repository)
{
_repository = repository;
}
public HelloResponse GetHello(string name)
{
var response = new HelloResponse
{
Message = $"Hello, {name}! (Repository Version)",
CreatedAt = DateTime.Now,
Location = "Cazis, Switzerland"
};
_repository.Save(response);
return response;
}
public List<HelloResponse> GetHistory()
{
return _repository.GetAll();
}
}
The key shift here is subtle but important:
The Service no longer knows where data is stored.
3️⃣ Step 3: Organizational Changes (Program.cs)
We have added a new role to the system, so we must register it properly.
The Repository should keep memory, so it is registered as a Singleton. The Service can be recreated per request, since it no longer stores data.
using HelloFlow.Services;
using HelloFlow.Repositories;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Repository: Singleton (keeps memory)
builder.Services.AddSingleton<HelloRepository>();
// Service: Scoped (stateless worker)
builder.Services.AddScoped<HelloService>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapControllers();
app.Run();
This configuration makes responsibilities explicit and predictable.
🧠 Why This Version Is “Deep”
The real value of this structure appears later.
When you replace in-memory storage with a real database:
- The Service does not change
- The Controller does not change
- Only the Repository changes
This is the practical meaning of Separation of Concerns.
🧠 One-Sentence Summary
The person who cooks (Service) and the person who stores ingredients (Repository) are now completely separated.
What to Remember at This Stage
- Repository handles storage only
- Service handles business logic only
- Controller handles requests only
-
Program.csdecides who works with whom
✍️ My Notes & Reflections
- I feel that I have started to understand what a Singleton is. I am not fully confident yet, but this step made its purpose much clearer.
- It also made me think that C# is a language where clearly separating responsibilities between different components is especially important.
Top comments (0)