Introduction

In many C#/.NET applications, business services rely on common dependencies like logging, mapping, time management, or database repositories.
Injecting these repeatedly into every service can lead to boilerplate code, inconsistency and less room for dependancies with relevant business logic value.
Another drawback of this is that potential code scanners like Sonar could eventually raise some rules errors when some of your constructors injects too much parameters (which is the case by default in Sonar C# rules, 7 max allowed ๐Ÿ˜‰). And this rule break could potentially block your quality gate โŒ

To solve the issue, you can define a generic utility interface โ€“ IServiceUtils<T> โ€“ that centralizes common dependencies and encourages consistent structure across services ๐Ÿ’ช

This is a pretty simple and straightforward pattern, and in this post I’ll show you what it is and how to implement it.
Let’s go ๐Ÿš€


What is IServiceUtils<T> ?

IServiceUtils<T> is a generic utility interface designed to bundle commonly used services like:

  • ILogger<T> โ€” for structured logging
  • IMapper โ€” for object-to-object mapping
  • TimeProvider โ€” to get the current date/time in a testable way
  • IUnitOfWork โ€” for managing transactions and repositories

This interface is then injected into your business services so they have access to all utilities via a single property.

Defining IServiceUtils<T>

public interface IServiceUtils<T>
{
    ILogger<T> Logger { get; }
    IMapper Mapper { get; }
    TimeProvider TimeProvider { get; }
    IUnitOfWork UnitOfWork { get; }
}

The interface is pretty simple.
T is used as a generic parameter in order to be processed by the ILogger.

Of course, you can add more common dependancies if you want to !
Dependancies that I show you today are some that I found useful in my current projects, but your implementation is up to you.
Just make sure that you don’t inject too much dependancies in your utils too, or you will just move your initial issue ๐Ÿ˜‰

Implementing a Default ServiceUtils

public class ServiceUtils<T> : IServiceUtils<T>
{
    public ILogger<T> Logger { get; }
    public IMapper Mapper { get; }
    public TimeProvider TimeProvider { get; }
    public IUnitOfWork UnitOfWork { get; }

    public ServiceUtils(
        ILogger<T> logger,
        IMapper mapper,
        TimeProvider timeProvider,
        IUnitOfWork unitOfWork)
    {
        Logger = logger;
        Mapper = mapper;
        TimeProvider = timeProvider;
        UnitOfWork = unitOfWork;
    }
}

The implementation is also straightforward !

Using IServiceUtils in a Business Service

  • Without using IServiceUtils :
public class UserService
{
    private readonly IUserRulesHelper _userRulesHelper;
    private readonly IMapper _mapper;
    private readonly IUnitOfWork _unitOfWork;
    private readonly TimeProvider _timeProvider;
    private readonly ILogger<T> _logger;

    public UserService(
        ILogger<T> logger,
        IMapper mapper,
        TimeProvider timeProvider,
        IUnitOfWork unitOfWork, 
        IUserRulesHelper userRulesHelper)
    {
        _userRulesHelper = userRulesHelper;
        _mapper = mapper;
        _unitOfWork = unitOfWork;
        _timeProvider = timeProvider;
        _logger = logger;
    }

    public async Task<UserModel> CreateUserAsync(CreateUserModel createUserModel)
    {
        _logger.LogInformation("Creating user...");

        var user = _mapper.Map<UserModel>(createUserModel);
        _userRulesHelper.VerifyIsOverEighteen(user);
        createUserModel.CreatedAt = _timeProvider.GetUtcNow();

        await _unitOfWork.Users.AddAsync(createUserModel);
        await _unitOfWork.CommitAsync();

        return user;
    }
}
  • With IServiceUtils :
public class UserService
{
    private readonly IServiceUtils<UserService> _utils;
    private readonly IUserRulesHelper _userRulesHelper;

    public UserService(IServiceUtils<UserService> utils, IUserRulesHelper userRulesHelper)
    {
        _utils = utils;
        _userRulesHelper = userRulesHelper;
    }

    public async Task<UserModel> CreateUserAsync(CreateUserModel createUserModel)
    {
        _utils.Logger.LogInformation("Creating user...");

        var user = _utils.Mapper.Map<UserModel>(createUserModel);
        _userRulesHelper.VerifyIsOverEighteen(user);
        createUserModel.CreatedAt = _utils.TimeProvider.GetUtcNow();

        await _utils.UnitOfWork.Users.AddAsync(createUserModel);
        await _utils.UnitOfWork.CommitAsync();

        return user;
    }
}

As the ServiceUtils takes a T param, add your implementation in your DI using this specific syntax:

services.AddScoped(typeof(IServiceUtils<>), typeof(ServiceUtils<>));

Here we can see that we reduced the numbers of parameters injected in the constructors from 5 to 2 in our UserService. ๐Ÿฅณ

Thanks to this, the UserService has now more room to inject specific business related implementations, such as some user helpers or other implementations that contains real business value.

What are the benefits ?

  • Less Boilerplate: Avoid repeating constructor parameters in every service.

  • Consistency: All services get the same base dependencies in a unified way.

  • Testability: Can mock IServiceUtils easily in unit tests.

  • Extensibility: Add more dependencies in one place without changing all service constructors.

  • Responsability: Focus on your business logic, and reflect it in your ctor signature.


Conclusion

Sometimes, simple and obvious refactoring such as the pattern I showed you today can be easily missed, but those are the ones from which you get big benefits !
The IServiceUtils<T> interface is a pretty simple way to solve common pain points in service design โ€” like duplicated constructor dependencies, inconsistent service structure, or difficulty writing unit tests.

By centralizing shared concerns, you make your services cleaner, more maintainable, and easier to test. ๐Ÿฅณ

As always, Happy hacking ๐Ÿ‘จโ€๐Ÿ’ปโค๏ธ