ORM - Active Record Pattern vs Data Mapper Pattern
Published: 2023-12-20 07:58:18

Active record and Data mapper patterns are two different approaches in accessing data in database (which can be referred as a persistent storage).

**Active Record Pattern:**
In this approach, a table is viewed as a class/entity and an object instance is associated with each row in the table.
Creation, Updation and Deletion of an object associates to the corresponding row in the table.

Example in typeorm:
[Typeorm - orm that can run in NodeJs, Browser etc]
user.js - referred as user model which represents the user table in database.

import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Enitiy()
export class User extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;
  @Column()
  firstName: string;
  @Column() 
  lastName: string;
}

The extended class BaseEntity provides all methods with respect to the creation, updation and deletion of a record.

- Creation of a new record:
import { User } from '../models/user.js'

const user = new User();
user.firstName = 'John';
user.lastName = 'Doe';
await user.save();

**Data Mapper Pattern:**
In this approach, the query methods such as save, remove etc are defined in a separate class called "repositories".
By this way it achieves independency between the in-memory representation (the entities/models) and the persistent storage (which refers to the database), as the entities will not be aware of how the data is being stored in database.

Example in typeorm:
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Enitiy()
export class User {
  @PrimaryGeneratedColumn()
  id: number;
  @Column()
  firstName: string;
  @Column() 
  lastName: string;
}

Creation of a new record:
import { User } from '../models/user.js'

const userRepository = MyDataSource.getRepository(User);
const user = new User();
user.firstName = 'John';
user.lastName = 'Doe';
await userRepository.save(user);

**Comparison:**
When to use Data Mapper:
- Can be used in complex/larger applications where we have a complex domain logic (business logic)
- Can be used if we want a separation or independency between in-memory representation (domain layer) and persistent storage (database layer).

Example usecase - 
User along with the book he/she borrowed:
Domain object:
class User {
    private firstName: string;
    private lastName: string;
    private booksBorrowed: BooksBorrowed[];
    constructor(firstName: string, lastName: 
      string, booksBorrowed: BooksBorrowed[]) 
    {
       this.firstName = firstName;
       this.lastName = lastName;
       this.booksBorrowed = booksBorrowed;
    }
}

class BooksBorrowed {
    private bookId: string;
    private author: string;
    constructor(bookId: string, author: 
      string) {
                  this.bookId = bookId;
                  this.author = author;
    }
}

Database schema can be different from what we have defined in domain object.
Therefore save, fetch operations and complex logic can be handled independently without exposing it to the domain object.

As the domain object is independent of the database schema, data mapper can be used in cases where the database schema changes frequently.



When to use Active Record Pattern:
- Is suitable in case of a simpler application where it mostly involves basic CRUD operations.
- If we want the model (in-memory storage) to replicate the database schema (i.e no need for separation between the domain layer and database layer).