mardi 16 février 2021

NuGet-packaged C# library: interface and implementations separation — design pattern wanted

I'll explain the problem on logging service. There is our own ILogService interface with usual methods like Info, Debug, Error, etc. And there are its implementations like ConsoleLogService, Log4NetLogService, SeriLogService, etc which implement ILogService interface. Assume we keep all logging functionality in a standalone project.

Next, let's say our big product consists of 5 applications and 30 class libraries which are used by those applications. Logging is used everywhere, however class libraries deal with ILogService interface only. Libraries use logging, but they don't construct its implementation instances. Applications are responsible for that - apps should know about implementations and how to construct them. After an implementation is constructed, it can be passed into any library as ILogService interface. So, class libraries need to know about ILogService interface only, they shouldn't depend on its implementations.

Now, we want to pack our log service into NuGet package and push into our private repository. After that, anyone who needs logging service, can install this package and do logging. This package depends on all third-party packages required for logging implementations: log4net, SeriLog, etc. That's fine while we do this for our 5 apps. An app should know how to construct every type of ILogService implementation, so it's OK that we need to install log4net, SeriLog, etc NuGet packages into every app which needs logging.

However our class libraries do no need to know how to construct those implementations. All they need is ILogService interface. But when we install our logger NuGet package into a class library, it will also install all dependent packages, so every class library which needs just an interface, will be forced to install all dependent third-party packages as well. That doesn't look good, since well, we just need an interface.

We could overcome this by adding logger project into our solutions and link it to required libraries as a project reference. In that case we would need to install log4net, SeriLog, etc into logger projects only. However it's not the way we want, because we want to keep our logging service distribution in a NuGet package way. May be we want to provide this logger to another team and we don't want to share the source code.

The only way I can see is to split this package in two: the interface package and implementations package. If you want to construct instances of a logger then you install implementations package. If all you need is ILogService interface only, then you just install the interface package.

However splitting packages in two every time when you want to separate the interface from its implementations doesn't feel right. So, what is the best way? Is there some well-known design pattern which allows to save us from redundant dependencies if all we need is an interface? Note that we want to distribute out logger via NuGet package(s).

Aucun commentaire:

Enregistrer un commentaire