반응형
Notice
Recent Posts
Recent Comments
관리 메뉴

간단한 개발관련 내용

트랜잭션 스크립트 패턴 VS 액티브 레코드 패턴 본문

Computer Science/Architechture & Design Patterns

트랜잭션 스크립트 패턴 VS 액티브 레코드 패턴

vincenzo.dev.82 2024. 12. 17. 15:45
반응형

1. 트랜잭션 스크립트 (Transaction Script)

구현 방법

트랜잭션 스크립트는 비즈니스 로직을 서비스 계층에서 직접 구현하는 패턴입니다.
하나의 트랜잭션 단위(Use Case)를 처리하는 스크립트 형태로 동작합니다.

구현 시:

  • 애플리케이션 서비스에서 데이터베이스 접근 코드와 비즈니스 로직을 함께 작성합니다.
  • 일반적으로 Service 클래스 내부에서 Repository를 호출하고 비즈니스 로직을 순서대로 나열합니다.

예시 코드 (Kotlin + Spring Boot):

@Service
class OrderService(
    private val orderRepository: OrderRepository,
    private val paymentGateway: PaymentGateway
) {
    @Transactional
    fun placeOrder(customerId: Long, productId: Long, amount: Double): Order {
        val order = Order(customerId = customerId, productId = productId, amount = amount, status = "PENDING")
        orderRepository.save(order)

        val paymentResult = paymentGateway.pay(customerId, amount)
        if (paymentResult.isSuccess) {
            order.status = "COMPLETED"
        } else {
            order.status = "FAILED"
        }
        orderRepository.save(order)

        return order
    }
}

사용하는 경우

  • 간단한 비즈니스 로직: 로직이 단순하고 복잡한 도메인 모델이 필요 없는 경우.
  • 작은 시스템: 비즈니스 규모가 작아 유지보수가 쉬운 시스템.
  • 일회성 트랜잭션: 한 번의 트랜잭션에서 모든 작업이 끝나는 경우 (예: 주문 생성 및 결제).

장점

  • 구현이 쉽고 직관적입니다.
  • 로직이 서비스 계층에 집중되어 있어 이해하기 쉽습니다.

단점

  • 비즈니스 로직 중복: 여러 Use Case에서 유사한 로직이 중복될 수 있습니다.
  • 도메인 모델의 빈약함: 비즈니스 로직이 엔티티가 아닌 서비스에 집중되므로 객체지향적인 설계와 거리가 멉니다.
  • 확장성 부족: 로직이 커지면 서비스 클래스가 비대해지고 유지보수하기 어려워집니다.

2. 액티브 레코드 (Active Record)

구현 방법

액티브 레코드는 도메인 객체(엔티티)데이터와 행위를 함께 포함하는 패턴입니다.
즉, 도메인 객체가 자신의 데이터를 데이터베이스에 저장하거나 조회하는 메서드를 가지고 있습니다.

구현 시:

  • 도메인 엔티티 자체가 CRUD와 비즈니스 로직을 모두 포함합니다.
  • 일반적으로 ORM(Object Relational Mapper)을 활용해 구현됩니다.

예시 코드 (Kotlin + JPA):

import jakarta.persistence.*

@Entity
class Order(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null,
    val customerId: Long,
    val productId: Long,
    var amount: Double,
    var status: String
) {
    @Transient
    private val repository = OrderRepository()

    fun completeOrder() {
        this.status = "COMPLETED"
        repository.save(this)
    }

    fun failOrder() {
        this.status = "FAILED"
        repository.save(this)
    }
}

Repository를 간단하게 도메인 모델 안에서 사용하는 경우:

class OrderRepository {
    fun save(order: Order): Order {
        // Save logic (DB connection or ORM)
        return order
    }
}

사용하는 경우

  • 간단한 CRUD 중심의 애플리케이션: 도메인 객체가 비즈니스 로직과 데이터 접근을 함께 처리해야 하는 경우.
  • ORM 프레임워크와 잘 맞는 환경: Rails, ActiveRecord ORM, JPA 등의 도구를 사용하는 경우.
  • 작은 시스템: 도메인 모델이 복잡하지 않고 객체 하나로 해결될 수 있는 경우.

장점

  • 간단한 코드: 도메인 객체 안에 데이터와 비즈니스 로직이 함께 있어 명확합니다.
  • 일관성: 객체가 자기 자신의 상태를 관리하고 저장할 수 있습니다.
  • 개발 속도: CRUD와 데이터 조작이 빠르게 구현됩니다.

단점

  • 단일 책임 원칙 위반: 도메인 객체가 데이터와 비즈니스 로직, 데이터 접근까지 모두 처리합니다.
  • 테스트 어려움: 객체 자체에 데이터 접근 로직이 있으므로 단위 테스트가 어렵습니다.
  • 확장성 문제: 비즈니스 로직이 복잡해지면 객체가 비대해집니다.

3. 트랜잭션 스크립트와 액티브 레코드 비교

구분 트랜잭션 스크립트 액티브 레코드
주요 개념 비즈니스 로직을 서비스 계층에 구현 비즈니스 로직과 데이터 접근을 엔티티에 포함
책임 분리 서비스 계층이 비즈니스 로직을 담당 엔티티가 데이터와 로직을 모두 담당
사용하는 계층 서비스 계층 도메인 모델 계층
유지보수성 비즈니스 로직이 많아지면 서비스 클래스가 비대해짐 엔티티에 로직이 많아지면 단일 책임 원칙 위반
적용 대상 복잡하지 않은 비즈니스 로직이 많은 경우 CRUD 중심의 작은 애플리케이션
테스트 용이성 서비스 계층만 테스트하면 되므로 상대적으로 쉬움 데이터 접근이 포함되어 단위 테스트가 어려울 수 있음
ORM 사용 ORM과는 분리된 서비스 계층 ORM과 밀접하게 연동
객체지향적 설계 객체지향적이지 않음 데이터와 행위를 객체에 포함해 객체지향적임

결론

  • 트랜잭션 스크립트:
    • 간단한 비즈니스 로직과 작은 시스템에 적합합니다.
    • 로직이 서비스 계층에 집중되므로 도메인 모델이 빈약해집니다.
  • 액티브 레코드:
    • CRUD 중심 시스템에서 개발 속도를 빠르게 올릴 수 있습니다.
    • 도메인 객체에 모든 책임이 몰리므로 복잡한 비즈니스 로직에는 부적합합니다.

어떤 방법을 선택해야 할까?

  • 단순 CRUD작은 프로젝트에서는 액티브 레코드를 사용하면 빠르게 개발할 수 있습니다.
  • 복잡한 비즈니스 로직이나 확장 가능한 시스템을 만들 때는 트랜잭션 스크립트 대신 도메인 모델 패턴(DDD) 사용을 고려해야 합니다.
반응형