일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 이노베이션아카데미
- psql extension
- 프라이빗클라우드
- 창업
- 레이캐스팅
- 정렬
- 동료학습
- 42seoul
- 스타트업
- 어셈블리
- schema first
- 쿠버네티스
- 어셈블리어
- raycasting
- 부동소수점
- 42서울
- 텍스트북
- 엣지컴퓨팅
- GraphQL
- uuid-ossp
- Cloud Spanner
- 자료구조
- enable_if
- SFINAE
- c++
- 파이썬
- mistel키보드
- adminbro
- 도커
- 스플릿키보드
- Today
- Total
written by yechoi
[TypeORM] Active Record, Data Mapper 비교 본문
Active Record
모델 안에 모든 쿼리 방식을 정해놓고, 모델 메소드를 활용해 오브젝트를 save-remove-load 하는 방식. 간단히 말하자면 모델 내부에 데이터 베이스에 접근하는 방식을 구현하는 패턴.
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;
}
모든 Active Record 엔티티는 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.find({ isActive: true });
const timber = await User.findOne({ firstName: "Timber", lastName: "Saw" });
BaseEntity
는 스탠다드 Repository
의 메소드 대부분을 가지고 있다. active recorde 엔티티를 다루기 위해서 Repository
나 EntityManager
를 사용할 필요가 없다.
성과 이름으로 유저의 정보를 리턴하는 함수가 있다고 하자. 우리는 이를 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
Data Mapper 방식에선 쿼리 메소드를 "레포지토리"라고 불리는 각각 별도의 클래스에 정의해야 한다. 그리고 해당 레포지토리를 사용해 객체를 save-remove-load한다. data mapper 방식에서 당신의 엔티티는 멍청하다. 그저 속성을 정할 뿐이고, 몇개의 더미 메소드를 가질 수도 있다.
간단히 말하자면 data mapper는 데이터 베이스를 모델 대신 레포지토리에서 접근하는 방식이다.
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
isActive: boolean;
}
이러한 엔티티를 사용하는 방식이다.
const userRepository = connection.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.find({ isActive: true });
const timber = await userRepository.findOne({ firstName: "Timber", lastName: "Saw" });
성과 이름으로 user를 반환하는 함수를 만들어보자. 이러한 함수는 "커스텀 레포지토리"에 만들어야 한다.
import {EntityRepository, Repository} from "typeorm";
import {User} from "../entity/User";
@EntityRepository()
export class UserRepository extends Repository<User> {
findByName(firstName: string, lastName: string) {
return this.createQueryBuilder("user")
.where("user.firstName = :firstName", { firstName })
.andWhere("user.lastName = :lastName", { lastName })
.getMany();
}
}
사용하려면 이렇게.
const userRepository = connection.getCustomRepository(UserRepository);
const timber = await userRepository.findByName("Timber", "Saw");
어떤 걸 골라야 할까
결정은 당신의 몫이다. 두가지 방식 모두 나름의 장단이 있다.
소프트웨어 개발에서 우리가 명심해야 할 한가지는 어떻게 우리의 어플리케이션을 유지보수할 것인지다. Data Mapper
방식은 유지보수성이 좋으며 큰 앱에서 더 효과적이다. Active Record
는 작은 앱에서 간단하게 만드는 데 적합하다. 그리고 간단함은 언제나 더 좋은 유지보수성을 만드는 열쇄다.