Module Provider Pattern¶
When developing Framework Modules, it is quite common to have a set of base functionality that is shared between different implementations of a module.
A clear example of this is the EntityFrameworkCoreModule
, which includes
everything needed to integrate Entity Framework with the Suite, and it's
provider includes the implementations for a specific database.
Implementation¶
There are no tools or utility classes to implement the Provider Pattern, it is just a set of conventions we have defined.
Let's define some terms:
- Main Module: the module that includes abstractions to be split between providers.
- Provider Module: a module that is specific for a particular provider, implementing the Main Module's abstractions.
So, for implementing the Provider Pattern, you will define your interfaces in your Main Module. These should include the methods/properties that you need to make specific for each provider.
Then, create a module for each Provider in a separate assembly. The Provider Module depends on the Main Module and defines implementations for the Main Module's abstractions.
The Main Module relies on Dependency Injection for discovering the abstraction's implementation. Meaning, it injects the abstractions and expects some module to have provided the implementations.
The Provider Module is encouraged to simply use attributes for registering the implementations as the typed interfaces.
Example¶
Say we want to create a module for Logging. We'll name it LoggingModule
. We
don't really need any dependencies for now.
However, we will define an interface:
C# | |
---|---|
Note that by adding the attribute on the interface level, we don't need to do
anything on the implementation for it to be registered with DI. Whenever we need
a logger, we would inject ISuiteLogger
and it just works, if we have an
implementation of course..
In order to support multiple logger providers, we have split our implementation into multiple projects, implementing the Module Provider Pattern.
We'll create another module named LoggingDefaultProviderModule
, that will
include a default implementation and will depend on the LoggingModule
.
C# | |
---|---|
Then, inside the new provider module, we will implement our interface:
C# | |
---|---|
Since the interface already includes the attributes for dependency injection registration, that's all we need to do.
Consumers of the module may depend on the Main Module and the Provider directly, or they may just depend on the Provider, which in turn depends on the Main Module.
Consumers may also depend on the Main Module only, and then depend on the provider implementation in runtime, by using External Modules.