[FIXED] Design Patterns for Data Access Layer

Issue

I have an application which uses a database (MongoDB) to store information. In the past I have used a class full of static methods to save and retrieve data but I have since realised this is not very object-oriented-ish or future proof.

Even though it is very unlikely I will change database I would rather something that does not tie me too strongly to Mongo. I would also like to be able to cache results with the option to refresh the cached object from the database but this is not essential and can be done in another place.

I have looked at data access objects but they do not seem very well-defined and I can’t find any good examples of implementation (In Java or a similar language). I also have many one off cases such as finding usernames for tab completion which seem not well suited and would make the DAO large and bloated.

Are there any design patterns that would facilitate getting and saving objects without being too database specific? Good examples of implementation would be helpful (preferably in Java).

Solution

Well, the common approach to data storage in Java is, as you noted, not at all very object-oriented. This is in and of itself neither bad nor good: "object-orientedness" is neither an advantage nor disadvantage, it’s just one of many paradigms, that sometimes helps with good architecture design (and sometimes not).

The reason DAOs in Java aren’t usually object-oriented is exactly what you want to achieve – relaxing your dependency on the database. In a better-designed language, that allowed for multiple inheritances, this, of course, can be done very elegantly in an object-oriented way, but with Java, it just seems to be more troublesome than it is worth.

In a broader sense, the non-OO approach helps decouple your application-level data from the way it is stored. This is more than (non-)dependency on the specifics of a particular database, but also the storage schemas, which is especially important when using relational databases (don’t get me started on ORM): you can have a well-designed relational schema seamlessly translated into the application OO model by your DAO.

So, what most DAOs are in Java nowadays are essentially what you mentioned in the beginning – classes, full of static methods. One difference is that, instead of making all the methods static, it is better to have a single static "factory method" (probably, in a different class), that returns a (singleton) instance of your DAO, which implements a particular interface, used by application code to access the database:

public interface GreatDAO {
    User getUser(int id);
    void saveUser(User u);
}
public class TheGreatestDAO implements GreatDAO {
   protected TheGreatestDAO() {}
   ... 
}
public class GreatDAOFactory {
     private static GreatDAO dao = null;
     protected static synchronized GreatDao setDAO(final GreatDAO d) {
         final GreatDAO old = dao;
         dao = d;
         return old;
     }
     public static synchronized GreatDAO getDAO() {
         return dao == null ? dao = new TheGreatestDAO() : dao;
     }
}

public class App {
     void setUserName(final int id, final String name) {
          final GreatDAO dao =  GreatDAOFactory.getDao();
          final User u = dao.getUser(id);
          u.setName(name);
          dao.saveUser(u);
     }
}

Why do it this way as opposed to static methods? Well, what if you do decide to switch to a different database? Naturally, you’d create a new DAO class, implementing the logic for your new storage. If you were using static methods, you would now have to go through all your code, accessing the DAO, and change it to use your new class, right? This could be a huge pain. And what if then you change your mind and want to switch back to the old DB?

With this approach, all you need to do is to change the GreatDAOFactory.getDAO() and make it create an instance of a different class, and all your application code will be using the new database without any changes.

In real life, this is often done without any changes to the code at all: the factory method gets the implementation class name via a property setting, and instantiates it using reflection, so, all you need to do to switch implementations is to edit a property file. There are actually frameworks – like spring or guice – that manage this "dependency injection" mechanism for you, but I won’t go into details, first, because it is really beyond the scope of your question, and also, because I am not necessarily convinced that the benefit you get from using those frameworks is worth the trouble integrating with them for most applications.

Another (probably, more likely to be taken advantage of) benefit of this "factory approach" as opposed to "static" is testability. Imagine that you are writing a unit test that should test the logic of your App class independently of any underlying DAO. You don’t want it to use any real underlying storage for several reasons (speed, having to set it up, and clean up afterwards, possible collisions with other tests, the possibility of polluting test results with problems in DAO, unrelated to App, which is actually being tested, etc.).

To do this, you want a test framework, like Mockito, that allows you to "mock out" the functionality of any object or method, replacing it with a "dummy" object, with predefined behavior (I’ll skip the details, because, this again is beyond the scope). So, you can create this dummy object replacing your DAO, and make the GreatDAOFactory return your dummy instead of the real thing by calling GreatDAOFactory.setDAO(dao) before the test (and restoring it after). If you were using static methods instead of the instance class, this would not be possible.

One more benefit, which is kinda similar to switching databases I described above is "pimping up" your DAO with additional functionality. Suppose that your application becomes slower as the amount of data in the database grows, and you decide that you need a cache layer. Implement a wrapper class, that uses the real DAO instance (provided to it as a constructor parameter) to access the database, and caches the objects it reads in memory so that they can be returned faster. You can then make your GreatDAOFactory.getDAO instantiate this wrapper, for the application to take advantage of it.

(This is called "delegation pattern" … and seems like a pain in the butt, especially when you have lots of methods defined in your DAO: you will have to implement all of them in the wrapper, even to alter behavior of just one. Alternatively, you could simply subclass your DAO, and add caching to it this way. This would be a lot less boring coding upfront, but may become problematic when you do decide to change the database, or, worse, to have an option of switching implementations back and forth.)

One equally broadly used (but, in my opinion, inferior) alternative to the "factory" method is making the dao a member variable in all classes that need it:

public class App {
   GreatDao dao;
   public App(final GreatDao d) { dao = d; }
}

This way, the code that instantiates these classes needs to instantiate the DAO object (which could still use the factory) and provide it as a constructor parameter. The dependency injection frameworks I mentioned above, usually do something similar to this.

This provides all the benefits of the "factory method" approach, that I described earlier, but, as I said, is not as good in my opinion. The disadvantages here are having to write a constructor for each of your app classes, doing the same exact thing over, and over, also not being able to instantiate the classes easily when needed, and some lost readability: with a large enough code base, a reader of your code, not familiar with it, will have hard time understanding which actual implementation of the DAO is used, how it is instantiated, whether it is a singleton, a thread-safe implementation, whether it keeps state, or caches anything, how the decisions on choosing a particular implementation are made etc.

Answered By – Dima

Answer Checked By – David Marino (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published