Data Access Object (DAO)

Patrón arquitectónico

Prácticamente todas las aplicaciones de hoy en día, requiere acceso al menos a una fuente de datos, dichas fuentes son por lo general base de datos relacionales, por lo que muchas veces no tenemos problema en acceder a los datos, sin embargo, hay ocasiones en las que necesitamos tener más de una fuente de datos o la fuente de datos que tenemos puede variar, lo que nos obligaría a refactorizar gran parte del código. Para esto, tenemos el patrón Arquitectónico Data Access Object (DAO), el cual permite separar la lógica de acceso a datos de los Objetos de negocios (Bussines Objects), de tal forma que el DAO encapsula toda la lógica de acceso de datos al resto de la aplicación.

Problemática

Una de las grandes problemáticas al momento de acceder a los datos, es que la implementación y formato de la información puede variar según la fuente de los datos, además, implementar la lógica de acceso a datos en la capa de lógica de negocio puedes ser un gran problema, pues tendríamos que lidiar con la lógica de negocio en sí, más la implementación para acceder a los datos, adicional, si tenemos múltiples fuentes de datos o estas pueden variar, tendríamos que implementar las diferentes lógicas para acceder las diferentes fuentes de datos, como podrían ser: bases de datos relacionales, No SQL, XML, archivos planos, servicios web o REST, etc).

Un programador inexperto o con pocos conocimientos sobre arquitectura podría optar por crear en un solo método la lógica de negocio y la lógica de acceso a datos, creando un método muy difícil de entender y mantener.

Solo imagina que necesitas hacer un servicio que cree un nuevo usuario, para lo cual, se tendrá que validar que el nombre de usuario no esté repetido antes de permitir la creación del usuario:


public class UserService {  

    public void createUser(NewUserRequestDTO dto) {  
        Connection connection =   
            DriverManager.getConnection("connection url ...");  
            
        Statement queryStm = connection.createStatement();  
        PreparedStatement usernameStm = connection.prepareStatement(  
            "select 1 from users u where u.username = ?");  
        usernameStm.setString(1, dto.getUsername());  
        ResultSet usernameResult = usernameStm.executeQuery();  
        if(usernameResult.next()) {  
            throw new RuntimeException("Username already exists");  
        }  
            
        connection.setAutoCommit(false);  
        PreparedStatement stm = connection.prepareStatement(  
            "insert into users(id,username,password) values (?,?,?)");  
        stm.setLong(1, dto.getId());  
        stm.setString(2, dto.getUsername());  
        stm.setString(3, dto.getPassword());  
        stm.executeUpdate();  
        connection.commit();  
    }  
}  
                    

Observa que las únicas líneas que obedecen a la lógica de negocio son de la 12 a la 14, donde validamos que no esté repetido el usuario, y podríamos decir que la 22 donde guardamos el nuevo usuario, todo lo demás es solo para establecer la conexión a la base de datos, y eso que solo estamos consultando un registro e insertando otro, imagina si esta operación fuera una transacción más grande que involucrar varias validaciones y entidades; seguramente este método crecería exponencialmente de tamaño y la complejidad para comprenderlo y darle mantenimiento también crecería.

Solución

Dado lo anterior, el patrón Data Access Object (DAO) propone separar por completo la lógica de negocio de la lógica para acceder a los datos, de esta forma, el DAO proporcionará los métodos necesarios para insertar, actualizar, borrar y consultar la información; por otra parte, la capa de negocio solo se preocupa por lógica de negocio y utiliza el DAO para interactuar con la fuente de datos.

Un error común al implementar este patrón es no utilizar Entidades y en su lugar, regresar los objetos que regresan las mismas API’s de las fuentes de datos, ya que esto obliga al BusinessObject tener una dependencia con estas librerías, además, si la fuente de datos cambia, también cambiarán los tipos de datos, lo que provocaría una afectación directa al BusinessObject.

Si aplicamos el nuevo conocimiento adquirido para actualizar nuestro servicio de creación de usuario, podemos ver una clara diferencia:


public void createUser(NewUserRequestDTO dto) {  

    UserDAO userDAO = new UserDAOImpl();  
        
    if(userDAO.findByUsername(dto.getUsername()) != null) {  
        throw new RuntimeException("Username already exists");  
    }  
        
    UserConverter usrConverter = new UserConverter();  
    User user = surConverter.fromDto(dto);  
    
    usrDAO.save(user);  
}  
                    

Observa el cambio significativo que hemos logrado utilizando un DAO, podemos ver que en lugar de preocuparnos por cómo crear la conexión a la base de datos y como consultar y guardar los datos, solo nos centramos en las reglas de negocio, por lo tanto, el código se hace mucho más fácil de entender y mantener.

Si solo tenemos una fuente de datos esta podría ser la solución definitiva, sin embargo, seguimos teniendo un problema, y es como sabemos exactamente que instancia del DAO instanciar, por ejemplo, en la línea 3 estamos creado una instancia de UserDAOImpl en código duro, por lo que si queremos cambiar de fuente de datos tendríamos que cambiar el código y con ello, un nuevo despliegue forzado, por ello, es inteligente utilizar el DAO en conjunto con el patrón Abstract Factory para que nos ayude a determinar y crear la instancia correcta del DAO según nuestra configuración.

Conclusiones

El patrón DAO es sin lugar a duda, unos de los más utilizados en la actualidad, ya que es fácil de implementar y proporciona claros beneficios, incluso, si solo tenemos una fuente de datos y esta no cambia, pues permite separar por completo la lógica de acceso a datos en una capa separada, y así, solo nos preocupamos por la lógica de negocio sin preocuparnos de donde viene los datos o los detalles técnicos para consultarlos o actualizarlos.

Por otro lado, mediante el patrón Abstract Factory es posible determinar en tiempo de ejecución que instancia concreta del DAO debemos instanciar, desacoplando la instancia concreta del DAO del BusinessObject, lo que permite cambiar la instancia concreta del DAO sin afectar el funcionamiento del BusinessObject.

Acerca de este libro

Introducción a la arquitectura de software

Todo lo que acabas de ver en este artículo es solo una pequeña parte del libro Introducción a la arquitectura de software, el libro más completo en español sobre arquitectura de software, donde cubrimos los temas más importantes para convertirte en un arquitecto de software profesional.

¿Quieres convertirte en arquitecto de software pero no sabes cuál es el camino adecuando? o simplemente no sabes que guía estudiar para convertirte en arquitecto de software, te invito a que veas mi libro:

Ver libro
Todos los derechos reservados ©
Reactive programming
LinkedinYoutubeTwitterFacebook

© 2021, Copyright - Oscar Blancarte. All rights reserved.