DEV Community

Applying the Data Mapper Pattern in a Customer Relationship Management System

Introduction
In the realm of enterprise application development, Martin Fowler's Patterns of Enterprise Application Architecture provides valuable patterns to tackle common challenges. One such pattern, the Data Mapper, effectively separates domain logic from database access, enhancing maintainability and testability. This article presents a real-world example of implementing the Data Mapper pattern within a Customer Management System.

Overview of the Data Mapper Pattern
The Data Mapper pattern serves to map objects in an application’s domain model to database records. By providing a layer of separation, it ensures that domain objects remain unaware of the database structure or data storage mechanisms. This approach leads to cleaner, more maintainable code.

Real-World Example: Customer Management System
Step 1: Define the Domain Model First, we establish a Customer class that embodies 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

}

Step 2: Create the Data Mapper Next, we create the CustomerDataMapper, responsible for handling database interactions:

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

}

Step 3: Utilizing the Data Mapper in Business Logic The application can now utilize the CustomerDataMapper to handle database interactions seamlessly:

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 remains independent of database details, promoting cleaner code.
Testability: Allows for unit testing of the domain model without a database connection.
Scalability: Changes in the database schema do not directly affect the domain model.

Conclusion
The Data Mapper pattern effectively decouples domain logic from database access, leading to more maintainable, testable, and scalable code. This pattern proves particularly useful in applications with complex domain logic, making it a valuable tool for enterprise developers.

Top comments (0)