Search code examples
mysqlpostgresqldesign-patternsadapterfacade

Implementing with both Adapter Design Pattern and Facade Design pattern


I'm new to design patterns. I'm implementing a tool which can connect to different databases as user need. this is my code structure.

enter image description here

in controllers I have my API calls. Below I paste post APi call for get all databases in server

 @PostMapping("/allDatabases")
    public List<String> getDatabases(@RequestBody DatabaseModel db) 
        throws IOException, SQLException {
        
        return migrationInterface.getAllDatabases(db);
         

    }

for now I'm getting response by calling a method in interface inside service package. But when database server is change(ex: postgres,mysql) I have to use different queries.

Ex:

public class PostgresPreparedStatements {

    public PreparedStatement getAllDbs(Connection con) throws SQLException {
        
    return con.prepareStatement(
        "SELECT datname FROM pg_database 
             WHERE datistemplate = false;");
    }
    
    
}

This query is not working in MySQL database. So I'll keep deferent prepared statements for deferent databases. My idea is calling to a BaseAdapter from controller and check server type like below.

public class BaseAdapter {
    
    public void checkServerType(String server) {
        
        switch(server) {
            case "postgres" :
//               postgres functions
                break;
            case "mysql" :
//              mysql functions
                break;
            default: 
                break;
        }   
    }

}

I want to call PostgresConnector.java if server is postgres. from Connector I want to call Facade to call functions and related queries.

Any idea how to do this?

please note: For now I'm implementing this for postgres and MySQL,but in future this should work with any database.


Solution

  • Adapter pattern is not used when you want to add new behaviour such as new databases in your case. The goal of adapter class is to allow other class to access the existing functionality. Adapter converts the interface of one class into something that may be used by another class.

    It looks like BaseAdapter has a responsibility to choose SQL statement for different databases. We can paraphraze this responsibility like we want to have generated SQL query based on database. So it looks like we can replace this switch statement with HashTable(Java) or Dictionary(C#). And this HashTable(Java) or Dictionary(C#) can be a simple factory that creates SQL queries. And our generated SQL queries can be strategies for concrete database.

    So let's dive in code.

    It looks like this is a place where Strategy pattern can be used:

    Strategy pattern is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.

    Let me show an example via C#. I am sorry I am not Java guy, however I provided comments about how code could look in Java.

    We need to have some common behaviour that will be shared across all strategies. In our case, it would be just one GetAllDbs() method from different data providers:

    public interface IDatabaseStatement
    {
        IEnumerable<string> GetAllDbs();
    }
    

    And its concrete implementations. These are exchangeable strategies:

    public class PostgresDatabaseStatement : IDatabaseStatement // implements in Java
    {
        public IEnumerable<string> GetAllDbs()
        {
            return new [] { "PostgresDatabaseStatement" };
        }
    }
    
    public class MySQLDatabaseStatement : IDatabaseStatement // implements in Java
    {
        public IEnumerable<string> GetAllDbs()
        {
            return new[] { "MySQLDatabaseStatement" };
        }
    }
    
    public class SqlServerDatabaseStatement : IDatabaseStatement // implements in Java
    {
        public IEnumerable<string> GetAllDbs()
        {
            return new[] { "SqlServerDatabaseStatement" };
        }
    }
    

    We need a place where all strategies can be stored. And we should be able to get necessary strategy from this store. So this is a place where simple factory can be used. Simple factory is not Factory method pattern and not Abstract factory.

    public enum DatabaseName
    {
        SqlServer, Postgres, MySql
    }
    
    public class DatabaseStatementFactory
    {
        private Dictionary<DatabaseName, IDatabaseStatement> _statementByDatabaseName
            = new Dictionary<DatabaseName, IDatabaseStatement>()
            {
                    { DatabaseName.SqlServer, new SqlServerDatabaseStatement() },
                    { DatabaseName.Postgres, new PostgresDatabaseStatement() },
                    { DatabaseName.MySql, new MySQLDatabaseStatement() },
            };
    
        public IDatabaseStatement GetInstanceByType(DatabaseName databaseName) =>
            _statementByDatabaseName[databaseName];
    }
    

    and then you can get instance of desired storage easier:

    DatabaseStatementFactory databaseStatementFactory = new();
    IDatabaseStatement databaseStatement = databaseStatementFactory
        .GetInstanceByType(DatabaseName.MySql);
    IEnumerable<string> allDatabases = databaseStatement.GetAllDbs(); // OUTPUT: 
        // MySQLDatabaseStatement 
    

    This design is compliant with the open/closed principle.