Make application layer loosely coupled
You approach is fine, it is look like some sort of Facade design pattern, but I would recommend more traditional way.
You already have spring as IoC container, so all you have to do is to extract interfaces from your *DBManager
-classes and tell spring about your implementations. Then you will be able to inject an actual implementation using an interface, which in turn might resides in a totally different package. Therefore coupling will cease to exist.
Here is an example.
DataStorage contract:
package root;import java.util.List;public interface UserDataStorage { void saveUserData(List<String> rows);}
DocumentStorage contract:
package root;public interface UserDocumentStorage { void saveUserDocs(String docs);}
DataStorage implementation:
package root.domain;import org.springframework.stereotype.Service;import root.UserDataStorage;import java.util.List;@Servicepublic class MysqlDBManager implements UserDataStorage { @Override public void saveUserData(List<String> rows) { System.out.println("Saving data...."); }}
DocumentStorage implementation:
package root.domain;import org.springframework.stereotype.Service;import root.UserDocumentStorage;@Servicepublic class MongoDBManager implements UserDocumentStorage { @Override public void saveUserDocs(String docs) { System.out.println("Saving docs...."); }}
Then your service classes might look like this (please pay attention there is no reference to domain
package:
package root.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import root.UserDataStorage;import root.UserDocumentStorage;import java.util.Collections;@Servicepublic class MyService { private final UserDataStorage dataStorage; private final UserDocumentStorage documentStorage; @Autowired public MyService(UserDataStorage dataStorage, UserDocumentStorage documentStorage) { this.dataStorage = dataStorage; this.documentStorage = documentStorage; } public void callMe() { this.dataStorage.saveUserData(Collections.emptyList()); this.documentStorage.saveUserDocs("Document1"); }}
IMHO there is a better way, The interfaces help you to have loosely couple layers, here is an example:
interface DocsDBManager { void saveUserDocs(String docs);}interface UserDBManager { void saveUserData(List<String> rows);}interface DBManager extends DocsDBManager, UserDBManager {}
And you must to implement them:
class MongoDBManager implements DocsDBManager { public void saveUserDocs(String docs) { //..... }}class MysqlDBManager implements UserDBManager { public void saveUserData(List<String> rows) { //..... }}
And:
class DBManagerBase implements DBManager { @Autowired private DocsDBManager docsDBManager; @Autowired private UserDBManager userDBManager; public void saveUserData(List<String> rows) { userDBManager.saveUserData(rows); } public void saveUserDocs(String docs) { docsDBManager.saveUserDocs(docs); } }
And finally:
class Service { @Autowired private DBManager dBManager; //....... code here dBManager.saveUserData(rows); dBManager.saveUserDocs(docs);}