.NET Naming Best Practises: Services vs. Providers

BEN ABT
medialesson
Published in
3 min readJan 13, 2024

In many code bases there are Services and Providers - and often they do the same thing structurally; but what is the idea of a Service- and Provider-Classes?

Provider

Provider-Classes usually represent an abstraction of an external dependency, e.g. an external API, File Syste, external System etc. They are usually very specific and have a very specific interface.
The most important point: they do not contain any application logic! They are only there to encapsulate the external dependency.

Example: Weather API

A weather API initially has a Client; this client defines which endpoints this API makes available. Depending on this, clients are static - they are passed the HTTP client as a parameter or not; e.g. if you use Refit.

Since I am a big fan of Refit and recommend it for such scenarios, this example is also based on Refit:

public interface IWeatherClient
{
[Get("/weather")]
Task<WeatherResponseModel> GetWeather();

[Get("/weather/{zipCode}")]
Task<WeatherResponseModel> GetWeather(string zipCode);
}

A provider is now responsible for handling the overhead that a client needs — e.g. caching, authentication, or options and very importantly: an abstraction of potential exceptions!

public class WeatherProvider
{
private readonly IWeatherClient _weatherClient;
private readonly WeatherOptions _weatherOptions;

public WeatherProvider(IWeatherClient weatherClient, IOptions<WeatherOptions> weatherOptions)
{
_weatherClient = weatherClient;
_weatherOptions = weatherOptions.Value;
}

public async Task<WeatherResponseModel> GetWeather()
{
// add authentication
// add optional handlers

// add try/catch error handling
return await _weatherClient.GetWeather();
}
}

As Providers do not contain any application logic, but are only responsible for abstracting external dependencies, it is very often the case that providers can be registered as singletons in your DI. In most cases, Provider does not interact with any other services, providers or event systems.

Services

While providers are primarily responsible for technical abstraction, services are generally the opposite: Services contain the application logic. Services are either implemented as large, unfortunately often monolithic logic modules (e.g. “UserService”), or smaller as so-called use case services aka handlers (e.g. “CreateUserService / CreateUserHandler”).

public class CreateUserService
{
private IUserAccountRepository _userAccountRepository;
private IUserProfileRepository _userProfileRepository;

public CreateUserService(IUserAccountRepository userAccountRepository, IUserProfileRepository userProfileRepository)
{
_userAccountRepository = userAccountRepository;
_userProfileRepository = userProfileRepository
}

public Task<UserAccountId> CreateUser(string userName, string email)
{
// database operation
// ..


return createdUser;
}
}

Services usually interact with other components of an application (database, various providers, event system) and are therefore registered as scoped in the DI system.

Conclusion

There is a very clear separation between what providers are responsible for and what task services fulfill — you just have to implement it in your own architecture. If other code bases do not follow these basic rules, this is no excuse for your own architectural mistakes.

Autor

Benjamin Abt

Ben is a passionate developer and software architect and especially focused on .NET, cloud and IoT. In his professional he works on high-scalable platforms for IoT and Industry 4.0 focused on the next generation of connected industry based on Azure and .NET. He runs the largest german-speaking C# forum myCSharp.de, is the founder of the Azure UserGroup Stuttgart, a co-organizer of the AzureSaturday, runs his blog, participates in open source projects, speaks at various conferences and user groups and also has a bit free time. He is a Microsoft MVP since 2015 for .NET and Azure.

Originally published at https://schwabencode.com on January 13, 2024.

--

--