liveBook · Manning

Active record pattern

As explained in Unit Testing Principles, Practices, and Patterns

The Active Record pattern combines a domain object's business logic with its persistence operations against a database.

The Active Record pattern combines a domain object’s business logic with database retrieval and persistence. This is convenient in simple or short-lived projects, but commonly scales poorly because one class owns both responsibilities. section 7.2

Listing 7.1. Initial implementation of the CRM system
public class User
{
    public int UserId { get; private set; }
    public string Email { get; private set; }
    public UserType Type { get; private set; }

    public void ChangeEmail(int userId, string newEmail)
    {
        object[] data = Database.GetUserById(userId);             #1
        UserId = userId;
        Email = (string)data[1];
        Type = (UserType)data[2];

        if (Email == newEmail)
            return;

        object[] companyData = Database.GetCompany();             #2
        string companyDomainName = (string)companyData[0];
        int numberOfEmployees = (int)companyData[1];

        string emailDomain = newEmail.Split('@')[1];
        bool isEmailCorporate = emailDomain == companyDomainName;
        UserType newType = isEmailCorporate                       #3
            ? UserType.Employee                                   #3
            : UserType.Customer;                                  #3

        if (Type != newType)
        {
            int delta = newType == UserType.Employee ? 1 : -1;
            int newNumber = numberOfEmployees + delta;
            Database.SaveCompany(newNumber);                      #4
        }

        Email = newEmail;
        Type = newType;

        Database.SaveUser(this);                                  #5
        MessageBus.SendEmailChangedMessage(UserId, newEmail);     #6
    }
}

public enum UserType
{
    Customer = 1,
    Employee = 2
}

The initial User implementation retrieves itself, applies email-change rules, and communicates with database and messaging dependencies. section 7.2

Making database and messaging dependencies explicit through interfaces improves visibility but does not remove the testing burden: mocks remain necessary, fragile, and expensive to maintain. A cleaner boundary keeps out-of-process collaborators outside the domain model. section 7.2

Refactoring away from Active Record

Move loading and saving into an application service, construct User and Company through factories, and leave business rules in memory. The controller then coordinates retrieval, domain behavior, persistence, and messaging without making User responsible for the database.

How Unit Testing Principles, Practices, and Patterns covers it

How deep it goes
Defines the pattern, diagnoses its testing costs, and demonstrates refactoring toward services, factories, and in-memory domain objects.
Examples
Uses a User class that loads itself from Database, changes email state, saves data, and sends a message.
What you can do after
Evaluate when database coupling makes a domain class overcomplicated and refactor coordination into an application service.
How it connects
Connects Active Record with domain models, out-of-process collaborators, mocks, application services, factories, controllers, and hexagonal architecture.

Also explained in

Compare how 3 books explain active record pattern →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Unit Testing Principles, Practices, and Patterns. The section links point to the parts of the book it draws on.