Active Record vs Data Mapper

Minions·2023년 3월 29일
0

TypeORM

목록 보기
2/2
post-thumbnail

TypeORM은 Active Record와 Data Mapper pattern을 둘 다 사용할 수 있다고 하였다. 그리하여 나는 두 패턴에 대해 공부해보고 내가 사용할 패턴을 선택하려고 한다.

Active Record pattern

Active Record의 경우, 모든 Query Method를 모델 내에 정의하고 사용하여 객체를 저장, 삭제 및 로드할 수 있다. 즉, SQL을 직접 작성하지 않고 데이터를 조작할 수 있다는 말이다. 그게 어떻게 가능한 것일까? 간단한 예제를 통해 알아보려고 한다.

Example

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

@Entity()
export class User extends BaseEntity {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    firstName: string

    @Column()
    lastName: string

    @Column()
    isActive: boolean

    static findByName(firstName: string, lastName: string) {
        return this.createQueryBuilder("user")
            .where("user.firstName = :firstName", { firstName })
            .andWhere("user.lastName = :lastName", { lastName })
            .getMany()
    }
}

Active Record Entity들은 모델 클래스를 정의할 때 BaseEntity를 Extends(상속)하기에 Repository나 Entity Manager를 사용하지 않아도 된다. 또한, BaseEntity는 표준 Repository 클래스의 메소드를 대부분 가지고 있다.

그리고 위 정의된 User Method는 아래와 같이 사용할 수 있다고 합니다.

const timber = await User.findByName("Timber", "Saw")

Data Mapper pattern

이어서 Data Mapper Pattern에 대해 정리하려고 한다. Data Mapper의 경우, Repository'클래스에 모든 쿼리 메서드를 정의하고, 객체를 저장, 제거 및 로드할 수 있다. 즉, SQL을 작성하지 않고, 데이터를 조작하기 위해 모델이 아닌 Repository를 사용한다. 아래의 예제를 통해 알아보도록 한다.

Example

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

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    firstName: string

    @Column()
    lastName: string

    @Column()
    isActive: boolean
}

User Class는 따로 상속 받는 클래스가 없다. 그리고 User에 대한 Repository는 다음과 같이 사용한다.

const userRepository = dataSource.getRepository(User)

// example how to save DM entity
const user = new User()
user.firstName = "Timber"
user.lastName = "Saw"
user.isActive = true
await userRepository.save(user)

// example how to remove DM entity
await userRepository.remove(user)

// example how to load DM entities
const users = await userRepository.find({ skip: 2, take: 5 })
const newUsers = await userRepository.findBy({ isActive: true })
const timber = await userRepository.findOneBy({
    firstName: "Timber",
    lastName: "Saw",
})

이와 같이 Data Mapper의 경우, User 클래스를 선언하고 클래스에 대한 Repository를 가지고 와서 데이터를 조작하게 된다.

정리

위에 설명한 내용이 이외에 찾아본 정보를 토대로 이 글을 마무리 해보려고 한다.

Active Record 패턴은 객체와 데이터베이스 레코드를 1:1 매핑하는 방식으로 동작한다. 각각의 엔티티는 데이터베이스 레코드에 대응하며, 해당 엔티티는 데이터베이스 연산(CRUD)을 직접 수행할 수 있다. 이 방식은 간단하고 직관적이지만, 복잡한 애플리케이션에서는 유지보수성이 낮아지고 확장성이 떨어질 수 있다.

Data Mapper 패턴은 객체와 데이터베이스 레코드를 서로 분리하여 매핑하는 방식으로 동작한다. 이 패턴에서는 엔티티가 데이터베이스와 상호작용하는 코드를 포함하지 않으며, 대신 데이터 매퍼(Data Mapper) 객체를 통해 데이터베이스와 통신한다. 이 방식은 확장성과 유지보수성이 높으며, 엔티티와 데이터베이스 스키마를 서로 다른 방식으로 변경해도 서로에게 영향을 주지 않는다. 그러나 구현이 복잡하고 코딩량이 많아질 수 있다.

요약하자면, Active Record 패턴은 단순하고 직관적이며 작은 규모의 애플리케이션에서 유용하다. Data Mapper 패턴은 복잡한 애플리케이션에서 확장성과 유지보수성이 뛰어나며, 코드량이 증가하더라도 적용하는 데 유용하다.

출처

[TypeORM 공식 홈페이지]
https://typeorm.io/active-record-data-mapper#what-is-the-active-record-pattern

profile
Junior Software Deveoper

0개의 댓글