I am developing against a system which provides a web service for access to its database and exposes that through .NET classes. Our usual way of working is to create an instance of the web service class whenever we need to access the database and directly use that instance; this of course completely goes against IoC and creates mostly untestable code. I am now trying to devise a new standard way of working using IoC to enable us to write (more) SOLID code.
My current solution is this (not really well explained):
The web service is wrapped in a DatabaseConnection class that stores the web service object as a protected member and provides access to a number of commonly used general database calls.
When I need database access in an actual application, I derive from that class (calling the new class, say, ApplicationDatabaseConnection) and implement the needed database interactions in methods that then are able to call the web service.
This class is not used directly in the application but provides “connector” interfaces for the different parts of the application, each of which is represented by a controller/view model like class at the top level. Whenever one of these application functions is called (e.g. from the UI), the appropriate controller object is created and passed my ApplicationDatabaseConnection object as an implementation of the respective “connector” interface, so database access is properly encapsulated and decoupled at that point, as far as I can tell.
My issue with this is: While this is the first case where I found actual use for the ISP (interface segregation principle) in my own code (though I’m far from sure whether this actually sensible use of the concept), I’m afraid that this class might do too much and violate the SRP (single responsibility principle). Or is “only providing database access, though for a number of different consumers” a single responsibility?
To maybe make that a bit clearer, here’s roughly what the relevant classes would look like:
DatabaseConnection
protected m_webservice
public OftenUsedDatabaseAccess()
ApplicationDatabaseConnection : DatabaseConnection, IConnectorA, IConnectorB
public IConnectorA.RetrieveRecords(fieldValue)
public IConnectorB.WriteStuff(listOfStuff)
FunctionAController
private m_IConnectorA
FunctionBController
private m_IConnectorB
The alternatives I can think of don’t really seem ideal to me either.
To split up the database access functionality, the ApplicationDatabaseConnection class could only be a factory class with Create methods for the different connectors (behind IConnectorAFactory, IConnectorBFactory interfaces) – but there’s not really anything there to necessitate a factory pattern; there’s nothing I only know when instantiating the “controller” objects.
Also, the actual connector classes would essentially also need to be derivations of DatabaseConnection because they need the same basic abilities, and then (at the latest) the whole construct becomes rather ominous.
I suppose I have taken a wrong turn at some point in my thinking and am now completely on the wrong track. What should the structure of such a solution look like? Any push in the right direction will be greatly appreciated.
Edit:
@Tobias ‘ answer made me realize that I forgot an important detail: There are two different versions of the web service with pretty much the same abilities, but completely different APIs, which is one reason why it has to be encapsulated.
Another one is that if my logic classes access the web service directly (or via an interface), they are also concerned with constructing the web service queries in all their detail, which makes for much more tightly coupled code (the kind we’ve been producing so far) and violates SRP – hence the connector interface idea.
Many months later I know that the “ApplicationDatabaseConnection” approach not only violated SRP, but also OCP while additionally hindering modularization.
The route I eventually took was a bit similar to the “factory” alternative I described – the “main” DatabaseConnection object is of a subclass that also has a “Create” method that that takes factories for specific DatabaseConnection derivations and doesn’t care about what exactly it creates. That way each controller can request its own connection object, and controllers can be added that the main application doesn’t know about (i.e. via MEF).