Tres Amigos of Persistance (DAO - DAOFactory - BO)

Nowadays, even with all the good accumulated knowledge we have in design patterns, software engineers still tend to program classes where business logic is mixed with persistence logic. Even some experienced developers omit this important aspect of software architecture maybe for lack of knowledge, or just for the rush to start programming quickly. I consider the last reason is the most common of all.

Now why should we bother too much about this separation? well, the idea is not to develop a case for what is basic in software architecture:
multi-tier(layer) programming , but to help with some useful patterns to accomplish this fundamental aspect of a well design application. I think almost everyone is aware of this principle but I know for experience that for many it is not so obvious how we can meet all the details of persistence logic independence.

First let's start with the most known pattern: Data Access Object (DAO). Citing from one design patterns book I have [1]:

Problem:

You want to encapsulate data access and manipulation in a separate layer

Forces:

1. You want to implement data access mechanisms to access and manipulate data in a persistence storage.
2. You want to decouple the persistent storage implementation from the rest of your application.
3. You want to provide a uniform data access API for a persistent mechanism to various types of data sources, such as
RDBMS, LDAP, OODB, XML repositories, flat files, and so on.
4. You want to organize data access logic and encapsulate proprietary features to facilitate maintainability and portability.

The DAO classes will contain all the logic to connect for example to a database and get the needed data from the corresponding tables. One important aspect of this DAO is that any implementation should always avoid to return any persistence proprietary object. For example if someone codes a DAO where the returned object is a ResultSet, the class wouldn't be meeting the point #4. The application would be coupled to the JDBC implementation. That is why in the next UML diagram the returned object from the DAO is a "TransferObject".

We don't have to get into much details of TransferObject pattern but I can say with just having domain objects is enough to ensure decoupling with business and persistence layers.

Having implemented the DAO pattern in our app does not decouple in 100% the business layer from the persistence one. Imagine for example you have this DAO class and the client that consumes it:

  1. package com.foo.dao.jdbc;
  2.  
  3. class FooDAOJDBCImpl {
  4. public void updateFoo(Foo foo) {
  5. ...
  6. }
  7. }
  8.  
  9. public class FooClient {
  10. void updateChangesInFoo(Foo foo) {
  11. com.foo.dao.jdbc.FooDAOJDBCImpl fooDAOJDBCImpl = new com.foo.dao.jdbc.FooDAOJDBCImpl();
  12. fooDAOJDBCImpl.update(foo);
  13. }
  14. }

Notice that the client needs to instantiate directly the JDBC implementation. Even though it is hidden for the client how the DAO class internally updates the data in the data source, the client still knows that the implementation uses JDBC to persist data. If the JDBC implementation has to be replaced by another one, the client code will have to be updated to use the new DAO class.

To make our design more flexible to such type of possible changes, and also to have our code prepared for unit testing (use of mock DAO classes), we can marry our DAO pattern with the AbstractFactory pattern to have a child named DAOFactory. The DAOFactory class uses reflection (one way to do it) to instantiate the DAO class.

  1. public interface DAO {
  2.  
  3. }
  4.  
  5. public interface FooDAO extends DAO {
  6. public void update(Foo foo);
  7. }
  8.  
  9. package com.foo.dao.jdbc;
  10. public class FooJDBCImpl implements FooDAO {
  11. public void update(Foo foo) {
  12. ....
  13. }
  14. }
  15.  
  16. public class FooClient {
  17. void updateChangesInFoo(Foo foo) {
  18. FooDAO dao = (FooDAO) DAOFactory.getDAO("foo") ;
  19. dao.update(foo);
  20. }
  21. }

It is not until execution time that is known what DAO class will be used to execute the persistence method. The name of the classes can be stored in a properties file. If the implementation of the DAO class is changed, it will be totally transparent to the Client class.

  1. /**
  2. Properties in some file:
  3. foo=com.foo.dao.jdbc.FooDAOJDBCImpl
  4. foo2=com.foo.dao.jdbc.Foo2DAOJDBCImpl
  5.  
  6. **/
  7.  
  8. import java.io.FileInputStream;
  9. import java.io.FileNotFoundException;
  10. import java.io.IOException;
  11. import java.util.Properties;
  12.  
  13. public class DAOFactory {
  14.  
  15. private static props = new ();
  16. private static boolean loadedProperties = false;
  17. private static propertiesPath;
  18.  
  19. public static void init( propertiesPath) {
  20. propertiesPath = path;
  21. }
  22.  
  23. public static DAOFactory getDAO( name) {
  24. try {
  25.  
  26. Class daoClass = Class.forName( getClass( name ) );
  27. return (DAO) daoClass.newInstance();
  28. }
  29. catch ( e) {
  30. e.printStackTrace();
  31. return null;
  32. }
  33. catch ( e) {
  34. e.printStackTrace();
  35. return null;
  36. }
  37.  
  38. }
  39.  
  40. private static getClass( propertyName ) {
  41. className = null;
  42. try {
  43.  
  44. if ( !loadedProperties ) {
  45.  
  46. file = new ( propertiesPath );
  47. props.load( file );
  48. loadedProperties = true;
  49. }
  50.  
  51. className = props.getProperty( propertyName, "");
  52. if ( className.length() == 0)
  53. return null;
  54. }
  55. catch ( e) {
  56. e.printStackTrace();
  57. }
  58. catch ( e) {
  59. e.printStackTrace();
  60. }
  61. catch ( e) {
  62. e.printStackTrace();
  63. }
  64. return className;
  65. }
  66.  
  67. }

The last design pattern to complete our gang is the Business Object (BO). I'm still learning how to use it correctly, I just realized writing this post that I have some fixes to do in a current implementation I have. But anyways, one of the main purposes of this pattern is to separate the persistence logic from the business logic. Normally in our applications we have a complex conceptual model containing structured, interrelated composite objects. Those complex composite relationships between classes require a lot of logic just to persist. So to avoid mixing these two logic's, an intermediate layer between business logic and data access is created; the BO's layer.

Let's suppose we have a class Foo containing a list of Foo2 objects:

  1. public class Foo {
  2. private List<Foo2> foo2s;
  3. private someAttribute;
  4.  
  5. }
  6. public class Foo2 {
  7. private someAttribute;
  8. }

If we want to persist our Foo class, we create two BO classes. The FooBO is the main entry point to save all the composite objects contained inside
Foo2 domain class.

  1. public class FooBO {
  2.  
  3. public void saveFoo(Foo foo) {
  4. FooDAO fooDAO = (FooDAO) DAOFactory.getDAO("foo") ;
  5. fooDAO.saveBasicFooInfo(foo);
  6. Foo2BO foo2Bo = new Foo2BO();
  7.  
  8. for (Foo2 foo2 : foo.getFoo2s()) {
  9. foo2Bo.saveFoo2(foo2);
  10. }
  11. }
  12. }
  13.  
  14. public class Foo2BO {
  15. public void saveFoo2(Foo foo) {
  16. FooDAO2 fooDAO2 = (FooDAO2) DAOFactory.getDAO("foo2") ;
  17. fooDAO2.saveFoo2(foo2);
  18. }
  19. }

There can be different ways to implement any of the 3 patterns described in this post; nothing is written in stone in the programming field. The idea was to provide a quick look on these three main patterns. If anyone has anything interesting to add, comments are well welcome.

[1] Deepak Alur, John Crupi, Dan Malks. " Core J2EE Patterns, Best Practices and Design Strategies ", 2003. Pags: 462,463.