DEV Community

Marant7
Marant7

Posted on

Applying the Data Mapper Pattern in a Real-World Application

Introduction
The Patterns of Enterprise Application Architecture (PEAA), as described by Martin Fowler, provides a set of reusable solutions to common problems in enterprise software development. One of these patterns is the Data Mapper, which is widely used for separating the domain logic from the database access logic. This article will present a real-world example applying the Data Mapper pattern, highlighting its benefits and the problems it solves.

Overview of the Data Mapper Pattern
The Data Mapper pattern is used to map objects in an application's domain model to records in a database. It provides a layer of separation, ensuring that the domain objects do not need to know about the database structure or the way data is stored. This separation makes the codebase more maintainable and testable, allowing the domain logic to evolve independently from the database.

Real-World Example: Implementing a Data Mapper in a Customer Management System
Let's consider a simple Customer Management System where we have to manage customers' data using a relational database. Instead of tightly coupling the Customer object with database operations, we will use a Data Mapper to handle the conversion between the database rows and the domain objects.

Step 1: Define the Domain Model
First, we define a Customer class that represents our domain model:

public class Customer
{
public int Id { get; private set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
public string Email { get; private set; }

public Customer(int id, string firstName, string lastName, string email)
{
    Id = id;
    FirstName = firstName;
    LastName = lastName;
    Email = email;
}

public void UpdateEmail(string newEmail)
{
    Email = newEmail;
}
}
Enter fullscreen mode Exit fullscreen mode

The Customer class encapsulates all the business logic and data related to a customer.

Step 2: Create the Data Mapper
The Data Mapper class, CustomerDataMapper, is responsible for handling the database interactions and mapping them to the Customer object.

using System.Data.SqlClient;

public class CustomerDataMapper
{
private readonly string _connectionString;

public CustomerDataMapper(string connectionString)
{
    _connectionString = connectionString;
}

public Customer FindById(int id)
{
    using (var connection = new SqlConnection(_connectionString))
    {
        connection.Open();
        var command = new SqlCommand("SELECT Id, FirstName, LastName, Email FROM Customers WHERE Id = @Id", connection);
        command.Parameters.AddWithValue("@Id", id);

        using (var reader = command.ExecuteReader())
        {
            if (reader.Read())
            {
                return new Customer(
                    (int)reader["Id"],
                    reader["FirstName"].ToString(),
                    reader["LastName"].ToString(),
                    reader["Email"].ToString()
                );
            }
        }
    }

    return null;
}

public void Insert(Customer customer)
{
    using (var connection = new SqlConnection(_connectionString))
    {
        connection.Open();
        var command = new SqlCommand(
            "INSERT INTO Customers (FirstName, LastName, Email) VALUES (@FirstName, @LastName, @Email); SELECT SCOPE_IDENTITY();",
            connection);

        command.Parameters.AddWithValue("@FirstName", customer.FirstName);
        command.Parameters.AddWithValue("@LastName", customer.LastName);
        command.Parameters.AddWithValue("@Email", customer.Email);

        var id = Convert.ToInt32(command.ExecuteScalar());
        typeof(Customer).GetProperty("Id").SetValue(customer, id, null);
    }
}

public void Update(Customer customer)
{
    using (var connection = new SqlConnection(_connectionString))
    {
        connection.Open();
        var command = new SqlCommand(
            "UPDATE Customers SET FirstName = @FirstName, LastName = @LastName, Email = @Email WHERE Id = @Id",
            connection);

        command.Parameters.AddWithValue("@Id", customer.Id);
        command.Parameters.AddWithValue("@FirstName", customer.FirstName);
        command.Parameters.AddWithValue("@LastName", customer.LastName);
        command.Parameters.AddWithValue("@Email", customer.Email);

        command.ExecuteNonQuery();
    }
}
}
Enter fullscreen mode Exit fullscreen mode

In this example, the CustomerDataMapper is responsible for converting database rows into Customer objects (FindById method) and converting Customer objects into rows (Insert and Update methods).

Step 3: Utilizing the Data Mapper in Business Logic
Now that we have the Customer and CustomerDataMapper classes, the application can utilize the Data Mapper pattern to handle database interactions cleanly.

public class CustomerService
{
private readonly CustomerDataMapper _customerDataMapper;

public CustomerService(string connectionString)
{
    _customerDataMapper = new CustomerDataMapper(connectionString);
}

public Customer GetCustomerById(int id)
{
    return _customerDataMapper.FindById(id);
}

public void RegisterNewCustomer(string firstName, string lastName, string email)
{
    var customer = new Customer(0, firstName, lastName, email);
    _customerDataMapper.Insert(customer);
}

public void UpdateCustomerEmail(int customerId, string newEmail)
{
    var customer = _customerDataMapper.FindById(customerId);
    if (customer != null)
    {
        customer.UpdateEmail(newEmail);
        _customerDataMapper.Update(customer);
    }
}
}
Enter fullscreen mode Exit fullscreen mode

Benefits of Using the Data Mapper Pattern
Separation of Concerns: The domain model (Customer) is independent of database concerns, allowing cleaner and more maintainable code.
Testability: Since the database access is handled by a separate class, the domain model can be easily unit-tested without needing a database connection.
Scalability: Changes in the database schema do not affect the domain model directly, making it easier to adapt to evolving requirements.
Conclusion
The Data Mapper pattern is an effective solution for decoupling domain logic from database access, resulting in more maintainable, testable, and scalable code. It is particularly useful in applications where the domain logic is complex and needs to be kept separate from infrastructure concerns.

Top comments (0)