[ORM] Active Record Vs. Data Mapper pattern

Manta·약 4시간 전
0

TIL

목록 보기
19/19

ORM 이란?

Object-relational mapping의 약자로, 객체지향 프로그래밍 언어를 사용하여 호환되지 않는 타입 시스템 간에 데이터를 변환하는 프로그래밍 기법이다. 따라서 이러한 기법은 프로그래밍 언어로 다룰 수 있는 "가상 객체 데이터베이스(virtual object database)"를 만들어낸다.

쉽게 말해, ORM은 데이터베이스와 어플리케이션 사이에 있는 레이어라 볼 수 있다.

그리고 Active Record Pattern과 Data Mapper pattern은 ORM 아키텍쳐 패턴이다

Active Record Pattern이란?

AR 패턴의 객체의 인터페이스는 CRUD 기능을 하는 메서드들과 데이터베이스 테이블에 있는 컬럼에 해당하는 프로퍼티를 가진다.

AR 패턴은 데이터베이스에 있는 데이터에 접근하는 방식이다. 데이터베이스 테이블은 클래스로 래핑된다. 따라서, 객체 인스턴스는 테이블의 열 하나에 해당하게 된다. 객체를 만들고 save하게 되면 테이블에 새로운 열이 추가된다. 마찬가지로 객체를 불러오면 그 객체는 데이터베이스의 정보를 그대로 가지고 있고, 객체를 업데이트하면 그 객체에 대응되는 열이 업데이트된다. 이 wrapper 클래스는 테이블이나 뷰의 각각의 컬럼에 대한 accessor 메서드(모델에서 데이터를 가져올때나 데이터를 업데이트 할 때 사용되는 메서드 )와 속성을 가지고 있다.

AR 패턴 사용법

Active Record 패턴은 모델 내에서 데이터베이스에 액세스하는 접근 방식입니다.

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
}

모든 액티브 레코드 엔터티는 BaseEntity엔터티와 함께 ​​작업할 수 있는 메서드를 제공하는 클래스를 확장해야 합니다.

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

// example how to remove AR entity
await user.remove()

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

BaseEntity표준의 대부분 방법을 가지고 있습니다 . 대부분의 경우 또는 활성 레코드 엔터티와 함께 Repository​​사용할 필요가 없습니다 .RepositoryEntityManager

이제 이름과 성으로 사용자를 반환하는 함수를 만들고 싶다고 가정해 보겠습니다. 클래스에서 정적 메서드로 이러한 함수를 만들 수 있습니다 User.

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()
    }
}
const timber = await User.findByName("Timber", "Saw")

Data Mapper pattern이란?

이 패턴을 따르는 객체의 인터페이스는 데이터 저장소의 도메인 엔티티 타입을 나타내는 객체에 대한 CRUD 함수를 포함한다.

Data Mapper는 영구적인 데이터 저장소(예를 들어, 관계형 데이터베이스)와 인메모리 데이터 표현 (도메인 레이어) 간의 양방향 데이터 전송을 수행하는 데이터 엑세스 레이어이다. Data Mapper 패턴의 목표는 인메모리 표현과 영구적인 데이터 저장소를 서로 독립적으로 유지시키고 데이터 매퍼 역할을 수행하는 것이다. 이 데이터 엑세스 레이어는 하나 이상의 매퍼로 구성되며, 데이터 전송을 수행한다. 일반적인 매퍼(generic mappers)는 여러 도메인 엔티티 타입을 다루고, 전용 매퍼(dedicated mappers)는 하나 또는 몇개만 처리한다.

Data Mapper pattern 프레임워크

  • Java/.NET
    • MyBatis
    • Hibernate
  • Node.js / Typescript
    • Bookshelf.js
    • TypeORM
    • Massive.js
    • Prisma
    • Objection.js
    • MikroORM

DM 패턴 사용법

데이터 매퍼는 모델 대신 리포지토리 내에서 데이터베이스에 액세스하는 접근 방식입니다.

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

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

    @Column()
    firstName: string

    @Column()
    lastName: string

    @Column()
    isActive: boolean
}

이러한 엔터티를 사용하는 방법:

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",
})

Active Record vs. Data Mpper

data mapper는 어플리케이션의 비즈니스 도메인과 데이터를 저장하는 데이터베이스 사이에 있는 레이어를 의미한다. AR 패턴은 도메인과 데이터베이스 간의 갭을 최대한 매끄럽게 메꾸려고 하는 반면, DM 패턴은 둘 사이를 더 독립적으로 두려고 하는 것이다.

profile
공부할게 너무 만타🫠

0개의 댓글