Enterprise Design Patterns: Applying the Data Mapper pattern with Java

In this article, we will explore the Data Mappe pattern, one of the patterns proposed by Martin Fowler in his catalog Patterns of Enterprise Application Architecture. We will see how to implement it in Java without using a database, ideal for those who want to understand the concept without extra technical complications.

🧠 What is the Data Mapper Pattern?

It is an enterprise architecture pattern that separates business logic from data access logic. Unlike the Active Record pattern, where the same object knows how to save or load its data, Data Mapper introduces an intermediate layer (the “Mapper”) that handles transferring data between the domain model and the data source (in this case, simulated).

Why is it useful?

  • Avoids mixing business logic with persistence.
  • Improves maintainability and scalability of the system.
  • Facilitates writing unit tests (no need for a DB).
  • Allows changing the persistence mechanism without affecting domain classes.

Implementation in Java

Below is a complete example using Java, simulating persistence in memory with a HashMap.

Suggested Package Structure:

🧱 1. Model User.java

package model;

public class User {
    private int id;
    private String name;
    private String email;

    public User() {}

    public User(int id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // Getters y Setters

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

🧱 2. Interface UserMapper.java

package mapper;

import model.User;
import java.util.List;

public interface UserMapper {
    User findById(int id);
    List<User> findAll();
    void insert(User user);
    void update(User user);
    void delete(int id);
}

🧱 3. Implementation UserMapperImpl.java

package mapper;

import model.User;
import java.util.*;

public class UserMapperImpl implements UserMapper {
    private Map<Integer, User> database = new HashMap<>();

    @Override
    public User findById(int id) {
        return database.get(id);
    }

    @Override
    public List<User> findAll() {
        return new ArrayList<>(database.values());
    }

    @Override
    public void insert(User user) {
        database.put(user.getId(), user);
    }

    @Override
    public void update(User user) {
        if (database.containsKey(user.getId())) {
            database.put(user.getId(), user);
        }
    }

    @Override
    public void delete(int id) {
        database.remove(id);
    }
}

🧱 4. Test Class UserService.java

package service;

import mapper.UserMapper;
import mapper.UserMapperImpl;
import model.User;

public class UserService {
    public static void main(String[] args) {
        UserMapper userMapper = new UserMapperImpl();

        User u1 = new User(1, "Juan", "juan@gmail.com");
        userMapper.insert(u1);

        User u2 = new User(2, "Lucía", "lucia@gmail.com");
        userMapper.insert(u2);

        System.out.println("📋 Todos los usuarios:");
        userMapper.findAll().forEach(u -> 
            System.out.println(u.getId() + " - " + u.getName())
        );

        System.out.println("✏️ Actualizando usuario:");
        u1.setName("Juan Pérez");
        userMapper.update(u1);
        System.out.println("Nuevo nombre: " + userMapper.findById(1).getName());

        System.out.println("🗑️ Eliminando usuario con ID 2");
        userMapper.delete(2);

        System.out.println("✅ Usuarios actuales:");
        userMapper.findAll().forEach(u -> 
            System.out.println(u.getId() + " - " + u.getName())
        );
    }
}

Output

All users:
1 - Juan
2 - Lucía
Updating user:
New name: Juan Pérez
Deleting user with ID 2
Current users:
1 - Juan Pérez

You can see the code on GitHub: GitHub Project Link

The Data Mapper pattern is a powerful tool within the set of Enterprise Design Patterns, especially when aiming to maintain a clean and decoupled architecture. Separating data access logic from business logic not only improves the maintainability of the system but also facilitates its long-term evolution and scalability.

This Java example demonstrates how this pattern can be applied without the need for a real database, using in-memory structures to focus on conceptual understanding. This approach is particularly useful in educational environments, prototypes, or when validating the architecture before connecting to a more complex persistence system.

Adopting patterns like Data Mapper not only promotes good design but also trains the development mindset toward solid software engineering principles, which are key in real-world enterprise projects.

Leave a Reply