class ProductDataMapper {
Product fromTable(ProductTable table){...}
ProductTable toTable(Product table){...}
}
Encapsula a conversão entre objetos do modelo de persistência e do modelo de domínio
O propósito de um objeto DataMapper é encapsulaer a lógica necessária para produzir ou preencher um objeto do modelo de domínio com base num objeto do modelo de persistência. Pode também oferecer a operação contrária produzindo, ou preenchendo, um objeto do modelo de persistência com base num objeto do modelo de dominio.
É muito comum ter que traduzir o modelo de persistência para o modelo de dominio, e vice-versa. O DataMapper encapsua esta lógica de uma forma que pode ser utilizada multiplas vezes.
A implementação de um DataMapper é com um método para cada atividade.
class ProductDataMapper {
Product fromTable(ProductTable table){...}
ProductTable toTable(Product table){...}
}
Normalmente o DataMapper é usado com um Repository e é deixado para o repositório a criação dos objetos. O mapper recebe o objeto já criado e simplesmente preenche os dados:
class ProductDataMapper {
Product fromTable(ProductTable table, Product product){...}
ProductTable toTable(Product product, ProductTable table){...}
}
É ainda possível criar uma interface genérica:
interface DataMapper<M, T> {
M fromTable(T table, M product);
T toTable(M product, T table);
}
e aplicá-la quando necessário:
class ProductDataMapper implements DataMapper<Product, ProductTable>{
Product fromTable(ProductTable table, Product product){...}
ProductTable toTable(Product product, ProductTable table){...}
}
O conceito e implementação de um DataMapper é muito simples e próximo ao de um conversor. Contudo, conversões de dominio pode ser mais complexas e é possivel que um DataMapper necessite usar os serviços de um, ou mais, objetos DataMapper. A composição de *DataMapper normalmente acompanha a composição de entidades no modelo de dominio.
Em certos cenários e tecnologias o DataMapper é substituido por um simples conversor. A principio isto funciona, mas em casos particulares um conversor não é suficiente. O exemplo clássico é o mapeamento de objetos de enumeração (enums). Estes são objetos que - no código - referenciam várias opções para um valor e no banco correspondem a algum tipo de código - normalmente inteiros ou letras. O mapeamento de um enum para um inteiro corresponde a uma especificação de como o modelo de persistencia do banco corresponde com o modelo de objetos na linguagem, e um conversor pode não ser suficiente para atenter este mapeamento.
Um exemplo de um DataMapper de enum que contém mais informação que os objetos sendo mapeados
class ProductTypeDataMapper extends EnumDataMapper<ProductType, Integer>{
ProductTypeDataMapper(){
map(ProductType.SERVICE, 1);
map(ProductType.GOOD, 2);
}
}
Os objetos de DataMapper, ao contrário de conversores, poder conter informação que nenhuma dos objetos convertidos tem. No caso do exemplo, nem o número 2 tem informação que está relacionado a ProductType.GOOD , nem ProductType.GOOD tem informaão do numero 2
O padrão DataMapper se relaciona ao padrão Repository onde é usado para auxiliar a conversão de dados. Pode também ser usado em conjução ao padrão DAO mas o mapeamento seria entre objetos do modelo de persistência e objetos da API de comunicação com o banco de dados.
[1] Martin Fowler Patterns of Enterprise Application Architecture: Addison-Wesley Professional (2003)