728x90
반응형
5. 간단한 비즈니스 로직 구현
트랜잭션 스크립트 패턴
프레젠테이션으로부터 단일 요청을 처리하는 여러 프로시저를 모아, 비즈니스 로직을 구현
(시스템의 퍼블릭 인터페이스 - 마틴 파울러)
- 트랜잭션 스크립트 패턴 : 프로시저를 기반으로 시스템의 비즈니스 로직을 구성, 각 프로시저는 퍼블릭 인터페이스를 통해 시스템 사용자가 실행하는 작업 구현
- 각 프로시저는 간단하고 쉬운 절차지향 스크립트로 구현
- 각 프로시저는 트랜잭션 동작을 통해 각 작업이 성공/실패할 수 있고, 유효하지 않은 상태를 만들면 안된다.
DB.StartTransaction();
var job = DB.LoadNextJob();
var json = LoadFile(job.Source);
var xml = ConvertJsonToXml(json);
WriteFIle(job.Destination, xml.toString());
DB.MarkJobAsCompleted(job);
DB.Commit();
트랜잭션 동작 구현 실패
- case1) 전체를 아우르는 tx 없이, 여러 업데이트 처리
- 해결 : try-catch 를 통해 두 데이터 변경을 모두 포함하는 tx 생성
- 하지만, 다중 레코드 tx를 지원하지 않거나, 분산 tx에서 통합할수 없는 여러 저장 장치로 작업하는 경우에는 위 해결방법으로 해결 안된다.
분산 트랜잭션
ex) db의 데이터 변경 후, 메시지 버스에 메시지를 발행(알림)
- 만약 변경은 처리되었으나, 발행 전 오류가 발생할 경우 메시지 발행하지 못해 알림을 받지 못함
- 여러 저장 장치에 걸친 분산 트랜잭션의 경우 확장 어려움 / 오류 발생 가능성 높아 일반적으로 피하는 방법
- 해결 방안 : 1. CQRS 아키텍처 패턴(8장) / 2.아웃박스 패턴 (변경사항 커밋 후 안정적 메시지 발행)
암시적 분산 트랜잭션
public void Execute(Guid userId)
{
_db.execute("UPDATE Users SET visits=visits+1 Where user_id=@p1, userID);
}
- 여전히 잠재적으로 일관성 없는 상태로 이어질 수 있는 분산 트랜잭션
- (클라이언트) -> 1. Execute -> (LogVisit) -> 2.Update -> DB
- (클라이언트) <- 3. Return Result - Logvisit
메서드는 성공했으나 호출자에게 전달 실패 시나리오
- logvisit이 rest 의 일부이고 네트워크 중단 발생
- logvisit이 호출자가 동일한 프로세스에서 실행되고 있지만, 호출자가 logvisit 작업의 성공적 실행을 추적 전에 프로세스가 실패하는 경우
결과적으로 모두 사용자는 실패 가정후, logvisit 재호출하여 카운트 2증가
해결방법
- 트랜잭션 동작을 보장하는 법 : 멱등성 만들기 (같은 요청을 여러번 반복하더라도 그 결과는 매번 동일해야함)
public void Execute(Guid userId, long visits)
{
_db.execute("UPDATE Users SET visits=@p1 Where user_id=@p2, visits, userID);
}
- 낙관적 동시성 제어
- 작업 호출 전 카운터의 현재 값을 읽고 매개변수로 logVisit에 전달 이 떄, 호출자가 처음 읽은 값과 동일한 경우에만 카운터 값 업데이트 처리
public void Execute(Guid userId, long expectedVisits)
{
_db.execute("UPDATE Users SET visits=visits+1 Where user_id=@p1 and visits = @p2, userID, visits);
}
트랜잭션 스크립트를 사용하는 경우
- 비즈니스 로직이 단순한 절차적 작업처럼 매우 간단한 문제 도메인에 효과적 (ex_ ETL)
- 정의상 비즈니스 로직이 단순한 지원 하위 도메인에 적합
- 일반 하위 도메인과 같은 외부 시스템과 연동하기 위한 어댑터로 사용 / 충돌 방지 계층 일부로 사용
- 트랜잭션 스크립트의 장점 : 단순함
- 최소한의 추상화 -> 런타임 성능 최적화
- 비즈니스 로직이 복잡 -> tx 간 비즈니스 로직이 중복되기 쉽고, 중복된 코드가 동기화되지 않을 떄 일관성없는 동작 발생
- 결국 핵심 하위 도메인에는 트랜잭션 스크립트 사용 X
- 이러한 단순함으로 인해 때로는 안티 패턴 취급
액티브 레코드 패턴
DB 테이블 / 뷰의 행을 감싸고 DB 접근을 캡슐화하고 해당 데이터에 도메인 로직 추가하는 오브젝트
- 마틴 파울러
- 액티브 레코드 : 좀 더 복잡한 자료구조에서 비즈니스 로직 작동 가능 (물론 비즈니스 로직 단순한 경우에도 사용)
- 액티브 레코드라고 하는 전용 객체를 통해 복잡한 자료구조 구현
- 자료구조 외에도 CRUD 구현
- ORM 또는 데이터 접근 프레임워크와도 관련
- 액티브 레코드는 데이터 접근 로직 구현
트랜잭션 스크립트 패턴과의 차이
- 우선, 공통적으로 액티브 레코드 또한 트랝잭션 스크립트로 시스템의 비즈니스 로직 생성
- 차이점 :
- 액티브 레코드 : DB 직접 접근하는 것이 아닌 트랜잭션 스크립트가 액티브 레코드 객체 조작
- 작업이 완료되면 트랜잭션의 원자성으로 인해 작업 성공 or 실패
public void Execute(userDetails) {
try {
_db.StartTx();
var user = new User();
user.Name = userDetails.Name();
user.Email = userDetails.Email;
user.Save();
_db.Commit();
} catch {
_db.Rollback();
throw;
}
}
- 목적 : 메모리 상의 객체를 DB 스키마에 매핑하는 복잡성 숨기기
- 영속성 담당 외, 비즈니스 로직 포함 가능 (유효성 검사 및 데이터 조작 관련 비즈니스 절차)
- 즉, 액티브 레코드 객체의 고유한 기능은 자료구조와 동작(비즈니스 로직) 분리
액티브 레코드를 사용하는 경우
- 지원 하위 도메인, 일반 하위 도메인과 외부 솔루션의 연동, 모델 변환 작업에 적합
- 빈약한 도메인 모델 안티패턴으로도 불림
728x90
반응형
'책 > 도메인 주도 설계 첫걸음' 카테고리의 다른 글
도메인 주도 설계 첫걸음 - 8장 : 아키텍처 패턴 (계층형 /CQRS / 포트와 어댑터) (0) | 2023.06.06 |
---|---|
도메인 주도 설계 첫걸음 - 7장 : 이벤트 소싱 (0) | 2023.04.22 |
도메인 주도 설계 첫걸음 - 6장 : 복잡한 비즈니스 로직 다루기 (0) | 2023.04.14 |
도메인 주도 설계 첫걸음 - 3장, 4장 (0) | 2023.03.25 |
도메인 주도 설계 첫걸음 - 1장, 2장 (0) | 2023.03.05 |