I had this idea that I would achieve some good automation and separation of concerns as follows:
- Define an interface,
IDataProvider, in a class in aDataMuncherproject that needs to both consume and output data. - In separate projects, have different implementations of that interface, e.g., in
LunarData.csprojwould beLunarDataProvider : IDataProvider, and inSolarData.csprojwould beSolarDataProvider : IDataProvider. - In
DataMuncher, reflect over the assembly and linked assemblies and find all classes implementingIDataProvider. - New up instances of these found classes, asked them what data they can provide, and if their data matches up with what was similarly learned from
IDataConsumerclasses, request the appropriate data, and send it onward to the consumers.
The idea is that if I need to add a new data provider, I can do so with only the barest touch on any other projects. I just create a new project, add a class that implements IDataProvider, and whether I'm manufacturing the data (as might occur in a unit test), reading from a text file, connecting to a database, or calling a web API, or any other means of getting the data, as long as I return it in the form required by the interface, it can be consumed. Thus, the quirks of the data are not exposed to the rest of the system and no proper nouns are propagated to other parts when they shouldn't be.
However, I've run into a wee bit of a problem where the provider classes need to depend on the DataMuncher, but the DataMuncher needs to depend on the provider classes (in order to do what ultimately amounts to self dependency-injection).
So for this scenario to work I need at least 3 projects: one where the interface is defined, one where it's implemented (dependent), and one that is dependent on both of them that does what amounts to dependency injection, call this the DataHub project. But that seems like a lot of complexity.
One suggestion was made to just put the interface definition and the data source implementations all in the same project. This would enable the implementing project to have no outside dependencies, and would avoid the need for another coordinating project to which many projects must refer. It also reduces the number of projects in the solution, which is also a possible benefit. (E.g., DataProviders.cs would have all three: IDataProvider plus LunarDataProvider and SolarDataProvider.
What would you do in this scenario? I saw value in having one project per implementation, but I have to admit that I don't have a plethora of experience with various project structures that would have given me an idea of what works best. I also saw value in the division between projects forcing the separation of implementation internals from other projects, so that no one implementation would ever, even by mistake, use code or classes from another implementation. With one project each, a review of the project reference dependency graph could easily establish that the separate implementations are properly isolated from each other. This makes sure the code is truly modular and properly encapsulated.
I also considered, still with three projects, defining the interface in the DataHub, but this would prevent it, due to circular reference problems, from doing the reflection to find data sources—the recipient of the benefit of the interface, DataMuncher, would have to perform that task, and instead of LunarData having a reference to DataMuncher, it would be the other way around. This is now more references, and the DataHub class now also has to have a lot of projects referencing it (again, instead of the other way around). All these extra references suggest that perhaps the single project with interface and implementations is better.
Some guidance or ideas would be appreciated.