비즈니스 로직을 단계별 절차로 처리하는 설계 기법으로
객체 지향 설계를 하지 않고 하나의 트랜잭션으로 구성된 로직을
단일 함수나 스크립트에서 처리하는 구조의 디자인 패턴이다.
구성 요소는 트랜잭션 스크립트 메서드, 상태 클래스, 동작 클래스가 존재한다.
1. 트랜잭션 스크립트 메서드
##트랜잭션 스크립트 메서드 예시
public void transferMoney(Account fromAccount, Account toAccount, double amount) {
try {
// 1. 잔고 확인
if (fromAccount.getBalance() < amount) {
throw new IllegalArgumentException("잔액 부족");
}
// 2. 송금 처리
fromAccount.withdraw(amount);
toAccount.deposit(amount);
// 3. 결과 저장
db.save(fromAccount);
db.save(toAccount);
} catch (Exception e) {
// 오류 발생 시 롤백 처리
db.rollback();
throw e;
}
}
2. 상태 클래스
데이터를 저장하고 관리하는 역할을 한다.
트랜잭션 스크립트가 조작할 데이터를 제공
주로 객체의 속성을 정의
필요에 따라 getter/setter 포함 가능 및 기본 동작 (입,출금)을 포함
## 상태 클래스 예시
public class Account {
private double balance; // 계좌 잔액
// Getter: 잔액을 읽기
public double getBalance() {
return balance;
}
// Setter: 잔액을 설정 (필요에 따라 사용)
public void setBalance(double balance) {
this.balance = balance;
}
// 간단한 동작: 입금
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("입금 금액은 0보다 커야 합니다.");
}
this.balance += amount;
}
// 간단한 동작: 출금
public void withdraw(double amount) {
if (amount > balance) {
throw new IllegalArgumentException("잔액 부족");
}
this.balance -= amount;
}
}```
3. 동작 클래스
## 동작 클래스 예시
public class BankService {
public void transferMoney(Account fromAccount, Account toAccount, double amount) {
// 1. 잔고 확인
if (fromAccount.getBalance() < amount) {
throw new IllegalArgumentException("잔액 부족");
}
// 2. 송금 처리
fromAccount.withdraw(amount);
toAccount.deposit(amount);
// 3. 결과 저장
db.save(fromAccount);
db.save(toAccount);
}
public void reverseTransaction(Account fromAccount, Account toAccount, double amount) {
// 환불 처리
toAccount.withdraw(amount);
fromAccount.deposit(amount);
// 결과 저장
db.save(fromAccount);
db.save(toAccount);
}
}
위의 코드 예시를 보면 두 코드가 동일한거 아니냐라고 느낄 수 있는데
동작 클래스는 주요 기능을 관리하는 큰 툴이고 여러 개의 트랜잭션 스크립트를 포함 및 호출한다.
트랜잭션 스크립트는 동작 클래스 안에서 실행되는 세부적인 작업 단위다.
| 구분 | 동작 클래스 (Behavior Class) | 트랜잭션 스크립트 (Transaction Script) |
|---|---|---|
| 역할 | 여러 트랜잭션 스크립트를 포함하거나 호출하여 기능을 구현 | 하나의 작업(트랜잭션)을 처리하는 메서드 |
| 범위 | 시스템의 특정 기능(예: 송금 서비스)을 담당하는 큰 틀 | 특정 작업(예: 계좌 이체)을 처리하는 세부 구현 |
| 구성 요소 | 상태 클래스와 트랜잭션 스크립트를 조합하여 동작 수행 | 상태 클래스를 직접 조작하여 로직 실행 |
| 재사용성 | 여러 트랜잭션 스크립트를 호출해 조합 가능 | 단일 작업에 집중하므로 재사용성이 낮음 |
| 위치 | 서비스 계층(Service Layer) | 서비스 계층 내부의 메서드 또는 함수 |
| 구분 | 장점 | 단점 |
|---|---|---|
| 구현 용이성 | 초기 개발 속도가 빠르고 간단한 로직 처리에 적합 | 시스템이 커질수록 코드 중복과 관리 어려움이 발생할 수 있음 |
| 명확한 로직 흐름 | 각 트랜잭션이 독립적으로 작성되므로 이해하기 쉽고 직관적임 | 비즈니스 로직이 커지면 메서드가 복잡해지고 디버깅 및 테스트가 어려워질 수 있음 |
| 적합한 규모 | 작은 규모의 프로젝트나 간단한 시스템에 적합 | 확장성과 재사용성이 낮아 대규모 프로젝트에는 부적합 |
| 설계 단순성 | 절차 지향적 설계로 복잡한 도메인 모델 없이도 빠르게 구현 가능 | 도메인 모델 개념이 약해 객체 지향 설계의 장점을 활용하기 어려움 |
모놀리식 애플리케이션과 비슷한 경향으로 비즈니스 로직이 복잡해지면
관리하기 힘들다는 단점이 존재하므로 단순한 CRUD 작업이 아닌 이상
트랜잭션 스크립트 패턴과 같은 절차적 코드 작성은 지양하는게 좋을 것 같다.
도메인 모델 패턴 및 도메인 주도 설계에 대한 설명 링크
도메인 모델 패턴과 DDD란?