본문 바로가기
책/도메인 주도 설계 첫걸음

도메인 주도 설계 첫걸음 - 5장 : 간단한 비즈니스 로직 구현

by 존민 2023. 4. 14.
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증가

 

해결방법

  1. 트랜잭션 동작을 보장하는 법 : 멱등성 만들기 (같은 요청을 여러번 반복하더라도 그 결과는 매번 동일해야함)
public void Execute(Guid userId, long visits)
{
  _db.execute("UPDATE Users SET visits=@p1 Where user_id=@p2, visits, userID);
 }
  1. 낙관적 동시성 제어
  • 작업 호출 전 카운터의 현재 값을 읽고 매개변수로 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
반응형