Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Week 10] CQRS - 박준수 #53

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 88 additions & 1 deletion chap08/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,88 @@
# 8. 애그리거트 트랜잭션 관리
# 8. 애그리거트 트랜잭션 관리

### 8.1 애그리거트와 트랜잭션

- 트랜잭션 처리 방식

1. 선점 방식 (비관적 락) 2. 비선점 방식 (낙관적 락)

### 8.2 선점 잠금

- 한 트랜잭션에서 쓰기를 하는 동안 다른 트랜잭션에서는 잠금으로 대기
- JPA에서는 EntityManager는 LockModeType을 인자로 받는 find()메서드르 제공함

```java
entityManger.find(Order.class,LockModeType.PESSIMISTIC_WRITE)
```

- for update 쿼리를 이용해서 선점 잠금을 구현한다.

```java
@Lock(LockModeType.PESSIMISTIC_WRITE)
```

- 스프링 데이터 JPA는 @Lock 애너테이션을 사용해서 잠금 모드를 지정한다.

**교착상태**

- 선점 잠금에서 교착생태가 발생할 수 있음
- 스레드 1, 2에서 각각 자원을 선점한 상태에서 서로 상대가 가진 자원에 대해 선점 잠금을 시도할 경우 발생함
- 해결 방법
1. 잠금을 구할 때 최대 대기 시간을 지정해야 함
2. 각 잠금 순서를 일치시켜주면 데드락이 발생하지 않음

```java
// JPA 해결 방법
Map<String, Object> hints=new HashMap();
hits.put("javax.peristence.lock.timeout",2000);
Order order=entityManager.find(Order.class,LockModeType.PESSIMISTIC_WRITE)
```

- DBMS에 따라 힌트가 적용되지 않을 수가 있다.

```java
//스프링 데이터 JPA는 @QueryHints 애너테이션을 사용해서 쿼리 힌트를 작성할 수 있다.
@Lock(LockModeType.PESSIMISTIC_WRITE)
@QueryHints({
@QueryHint(name = "javax.peristence.lock.timeout", 2000)
}
)
@Query("selet m from Member m where m.id = :id)
Optional<Member>findByIdForUpdate(@Param("id) MemberId memberId)
```

### 비선점 잠금

- 동시에 접근하는 것을 막는 대신 변경한 데이터를 실제 DBMS에 반영하는 시점에 변경 가능 여부를 확인하는 방식이다.

```java
UPDATE aggtable SET version=version+1,colx=?,coly=?
WHERE aggid=?and version=현재 버전
```

- JPA는 버전을 이용한 비선점 잠금 기능을 지원한다. @Version 애너테이션을 붙이고 매핑되는 테이블에 버전을 저장할 칼럼을 추가하면 된다.

```java
public class Order {

@Version
private long version;

...
}
```

- 비선점 잠금 충돌 발생시 스프링 프레임워크에서 OptimisticLockingFailureException이 발생함

**강제 버전 증가**

- 루트 애그리거트의 값이 바뀌지 않았더라다도 애그리거트의 구성요소 중 일부 값이 바뀌면 논리적으로 그 애그리거트는 바뀐 것이다.
- 따라서 애그리거트 내의 어떤 구성요소의 상태가 바뀌면 루트 애그리거트이 버전 값이 증가해야 비선점 잠금이 올바르게 동작한다.
- JPA의 경우 LockModeType.OPTIMISTIC_FORCE_INCREMENT를 사용하면 해당 엔티티의 상태가 변경되엇는지에 상관엇이 트랜잭션 종료 시점에 버전 값 증가
처리를 한다.
- 스프링 데이터 JPA에서는 @Lock 애너테이션을 이용해서 지정하면 된다.

### 오프라인 선점 잠금

- 잠금 선점 시도, 잠금 확인, 잠금 해제, 잠금 유효시간 연장의 네 가지 기능이 필요하다
![img.png](img.png)
1 change: 0 additions & 1 deletion chap09/README.md

This file was deleted.

1 change: 0 additions & 1 deletion chap10/README.md

This file was deleted.

43 changes: 42 additions & 1 deletion chap11/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,42 @@
# 11. CQRS
# 11. CQRS


### 11. 1 단일 모델의 단점

- 시스템이 상태를 변경할 때와 조회할 때 단일 도메인 모델을 사용하기 때문
- 객체 지향으로 도메인 모델을 구현할 때 주로 사용하는 ORM 기법은 도메인 상태 변경 기능을 구현하는 데는 적합하지만 주문 상세 조회 화면처럼 여러 애그리거트에서 데이터를 가져와 출력하는 기능을 구현하기에는 고려할게 많아서 구현을 복잡하게 만드는 원인이 됨

→ 구현 복잡도를 낮추는 간단한 방법 : 상태 변경을 위한 모델과 조회를 위한 모델을 분리하는 것

### 11.2 CQRS

- Command Query Responsibility Segregation
- 상태를 변경하는 명령을 위한 모델과 상태를 제공하는 조회를 위한 모델을 분리하는 패턴
- CQRS를 사용하면 각 모델에 맞는 구현 기술을 선택할 수 있다.
- 명령 모델 : 객체지향에 기반해서 도메인 모델을 구현하기에 적당한 JPA,
- 트랜잭션을 지원하는 RDBMS
- 조회 모델 : DB 테이블에서 SQL로 데이터를 조회할 때 좋은 마이바티스
- 단순히 데이터를 읽어와 조회하는 기능은 응용 로직이 복잡하지 않기 때문에 컨트롤러에서 바로 DAO를 실행해도 무방하다.
- 조회 성능이 좋은 메모리 기반 NoSQL

### 11.2.1 웹과 CQRS

- 웹은 쓰기 요청 보다 조회 요청의 빈도수가 높음
- 메모리에 캐싱하는 데이터는 DB에 보관된 데이터를 그대로 저장하기보다는 화면에 맞는 모양으로 변환된 데이터를 캐싱할 때 성능에 더 유리하다.

### 11.2.2 CQRS 장단점

**장점**

- 명령 모델을 구현할 때 도메인 자체에 집중할 수 있다는 점
- 조회 성능을 향상시키는 데 유리하다는 점

**단점**

- 구현해야할 코드가 더 많음
- 더 많은 구현 기술이 필요함

**결론**

- 도메인이 복잡하지 않은데 CQRS를 도입하면 두 모델을 유지하는 비용만 높아지고 얻을 수 있는 이점은 없다.
- 반면 트래픽이 높은 서비스인데 단일 모델을 고집하면 유지 보수 비용이 오히려 높아질 수 있으므로 CQRS 도임을 고려하자.