Abstraction is a powerful concept in software design
Abstraction is a powerful concept in software design, but over-abstraction can inadvertently harm your project. Here's a real experience from a .NET Core (C#) project.
-> The Problem: Abstraction Done Too Early
In one of our enterprise .NET Core applications, we aimed for “perfect architecture” and created multiple interfaces for a simple CRUD-based User module, including:
- IUserService
- IUserManager
- IUserProcessor
- IUserRepository
- IBaseRepository<T>
- IReadOnlyRepository<T>
Example:
public interface IUserService
{
Task<UserDto> GetUserAsync(int id);
}
public class UserService : IUserService
{
private readonly IUserManager _userManager;
public UserService(IUserManager userManager)
{
_userManager = userManager;
}
public async Task<UserDto> GetUserAsync(int id)
{
return await _userManager.GetUserAsync(id);
}
}
This resulted in:
- No business logic
- Just method-to-method forwarding
- More files, more DI registrations, more confusion
-> Real Impact on the Project
- New developers took longer to understand the flow
- Debugging required jumping across 4–5 layers
- Change requests took more time
- “Flexible architecture” became hard to maintain
-> The Fix: Abstraction Where It Matters
We refactored by asking, “Is this abstraction solving a real problem today?”
What we kept:
- Repository abstraction (DB may change)
- Service layer only where business logic exists
-> What we removed:
- Pass-through interfaces
- Premature layers
-> Simplified version:
public class UserService
{
private readonly UserRepository _repository;
public UserService(UserRepository repository)
{
_repository = repository;
}
public async Task<UserDto> GetUserAsync(int id)
{
var user = await _repository.GetByIdAsync(id);
if (!user.IsActive)
throw new Exception("Inactive user");
return MapToDto(user);
}
}
- Clear
- Readable
- Easy to change
- Faster onboarding
-> Following are the important points:
- Abstraction is a tool, not a rule
- Don’t abstract until you see variation or change
- YAGNI still applies in modern .NET Core projects
- Simple code scales better than “clever” architecture
Good architecture evolves -> it is not forced on Day One.
Notice Inappropriate?
If you come across any inappropriate content, please report it to our administrators!