Pular para o conteúdo

DAO - Data Access Object

Embora popularizado pelo Core J2EE Patterns, o DAO é o parente pragmático dos padrões de mapeamento de dados de Fowler (Data Mapper, Gateway). Sua intenção:

Abstrair e encapsular todo acesso à fonte de dados, gerenciando a conexão e as operações de leitura/escrita, expondo uma interface de acesso independente da tecnologia.

Sem um DAO, código SQL/JDBC se espalha pela aplicação, acoplando lógica a detalhes de persistência e prejudicando a testabilidade. O DAO centraliza esse acesso e permite trocar a implementação (JDBC → JPA, ou um banco por outro) sem afetar clientes.

  • Acesso a dados orientado a CRUD por tabela/entidade.
  • Camadas de dados de aplicações Transaction Script ou serviços simples.
  • Quando você quer uma abstração fina e direta sobre a persistência, sem a semântica de agregados.
Vantagem Custo
Isola detalhes de persistência Tende a espelhar tabelas, não o domínio
Fácil de mockar em testes Interface CRUD “genérica” pode vazar por toda parte
Baixo acoplamento à tecnologia Não impõe fronteira de agregado/consistência

Esta é a distinção central da nota.

Aspecto DAO Repository
Orientação Tabela / linha Coleção de Agregados
Linguagem Do armazenamento (CRUD) Do domínio (ubíqua)
Granularidade Uma entidade/tabela Raiz de agregado
API típica insert, update, delete, findById add, findBy..., remove (semântica de coleção)
Consistência Não define fronteira Respeita a fronteira do agregado
Origem Core J2EE / camada de dados Domain-Driven Design
graph LR
    subgraph "Orientado a tabela"
        S1[Serviço] --> D[ContaDao]
        D --> T1[(tabela conta)]
    end
    subgraph "Orientado a domínio"
        S2[Serviço] --> R[ContaRepository]
        R --> AG[Agregado Conta + Movimentos]
        AG --> T2[(persistência)]
    end
public interface ContaDao {
Optional<ContaRow> findById(Long id);
List<ContaRow> findAll();
void insert(ContaRow conta);
void update(ContaRow conta);
void deleteById(Long id);
}
public record ContaRow(Long id, BigDecimal saldo, String status) {}
@Repository
public class JdbcContaDao implements ContaDao {
private final JdbcTemplate jdbc;
public JdbcContaDao(JdbcTemplate jdbc) {
this.jdbc = jdbc;
}
private static final RowMapper<ContaRow> MAPPER = (rs, n) ->
new ContaRow(rs.getLong("id"), rs.getBigDecimal("saldo"), rs.getString("status"));
@Override
public Optional<ContaRow> findById(Long id) {
return jdbc.query("SELECT id, saldo, status FROM conta WHERE id = ?", MAPPER, id)
.stream().findFirst();
}
@Override
public List<ContaRow> findAll() {
return jdbc.query("SELECT id, saldo, status FROM conta", MAPPER);
}
@Override
public void insert(ContaRow c) {
jdbc.update("INSERT INTO conta (id, saldo, status) VALUES (?, ?, ?)",
c.id(), c.saldo(), c.status());
}
@Override
public void update(ContaRow c) {
jdbc.update("UPDATE conta SET saldo = ?, status = ? WHERE id = ?",
c.saldo(), c.status(), c.id());
}
@Override
public void deleteById(Long id) {
jdbc.update("DELETE FROM conta WHERE id = ?", id);
}
}

A API é CRUD sobre linhas (ContaRow), sem comportamento nem fronteira de agregado — exatamente o que caracteriza o DAO frente a um Repository.

  • DAO genérico onipresente: um GenericDao<T> usado em todo o sistema, escondendo consultas específicas e vazando semântica CRUD para o domínio.
  • Chamar DAO de Repository: renomear a interface não muda a orientação a tabela; se a API é CRUD por linha, é DAO.
  • Regra de negócio no DAO: acesso a dados deve ser fino; lógica pertence ao domínio/serviço.
  • N+1 e falta de fronteira: sem agregado, é fácil carregar entidades relacionadas em laços.
  • Vazar ResultSet/detalhes JDBC através da interface.

1. Qual é a diferença fundamental entre DAO e Repository?

Resposta

O DAO é orientado a tabela/linha e fala a linguagem do armazenamento (CRUD); o Repository é orientado a coleção de agregados e fala a linguagem do domínio, respeitando fronteiras de consistência.

2. Em que estilo de arquitetura o DAO é mais natural?

Resposta

Sobre um Transaction Script ou serviços simples com acesso CRUD por tabela. Sobre um Domain Model rico, prefere-se o Repository.

3. Por que um GenericDao<T> pode ser um problema?

Resposta

Porque generaliza a semântica CRUD e a espalha por todo o sistema, escondendo consultas específicas e incentivando o vazamento de detalhes de persistência e a ausência de fronteiras de agregado.

4. Renomear um DAO para Repository o torna um Repository?

Resposta

Não. O que define é a orientação: se a API continua sendo CRUD sobre linhas/tabelas, ainda é um DAO, independentemente do nome.