samedi 14 mars 2020

Dynamic data source. Like Repository with multiple implementations

I'm creating a WPF desktop application that uses an SQL database for storing and loading its data, or a web API.

For the data layer I use the Repository design pattern.

The application has the option to use a local database, or to do exactly the same, but using a web API for handling the same data. In that last case the data all comes from a webserver. The data source the application is using, should be configurable per feature. So a feature that uses images from some data source, must look to the application settings to determine wether the images should be loaded from the local data source (local db) or from the web API. All this can be encapsulated in Repository classes, and the code that uses the repository only should be aware of the Repository interface, like an IPictureRepository interface.

So the interface is the only thing the outside world is aware of. But the different repository implementations need different dependencies. Always when I used the repository pattern in the past, I just injected the dependencies in the constructor. It was like so:

// variable pictureRepo is an interface type, so DatabasePictureRepository could 
// be any object implementing that interface.
// Normally I even don't put the word 'database' in the classname when I know there 
// will be only one data source (so I know it's coming from a database anyway).
IPictureRepository pictureRepo = new DatabasePictureRepository(dbInstance);

In above code, the concrete class DatabasePictureRepository is instantiated in the usercode, so not dynamic (like getting it from something like a container). But I didn't get problems with it, since the variable pictureRepo is an Interface type, I could just change only that line of code and instantiate another class (say APIPictureRepository implementing IPictureRepository). Without breaking any other code that uses the repository (via the pictureRepo variable).

For small apps that won't change a lot, the story is quite simple so far.

Now it is different. I want the following: the application can be configured to work in offline or online mode. In offline mode, the data source is the local database, the online mode uses my web API. (Don't worry about the possible data disagreement when the configuration changes between offline/online.)

This means the instantiation of all repositories changes, including the dependencies that are injected in the constructor.

I could think of the following solutions:

  1. Create a factory for each repository, so PictureRepositoryFactory or UserRepositoryFactory and the factory instantiates and returns the right repo implementation class, based on the app configuration.
  2. For each repository create only one Repository class that's visible for the outside (like just PictureRepository implements IPictureRepository) and inside that class, instantiate some sub repository based on the app config: if offline then instantiate some sub-repository like: DatabasePictureRepository : IPictureRepository. If online: APIPictureRepository : IPictureRepository and use that owned sub-repo to get the data, and return that data from the publicly visible PictureRepository. So the repo that is used from outside works as a portal. One downside is that all publicly used 'portal-repositories' will contain duplicated code for determining what sub-repo to use based on the app configuration.

What are some existing designs to create a good solution to my need, and does anyone have some suggestions?

Aucun commentaire:

Enregistrer un commentaire