Use Case
유즈케이스에는 애플리케이션의 비즈니스 로직을 작성합니다. 유즈케이스는 common.usecase의 UseCase 클래스를 확장해 구현합니다.
비즈니스 로직
모든 로직은 유즈케이스 내부에 작성되어야 합니다. 로직을 적절히 추상화해 유즈케이스의 execute 함수를 확인하는 것만으로 전체 로직을 파악할 수 있도록 만듭니다.
예시
Good 👍
class CleanupAwaitingProfileImages:
def call(self):
awaiting_profile_images = self._get_awaiting_profile_images()
if not awaiting_profile_images:
return
self._delete_awaiting_profile_images(awaiting_profile_images)
def _get_awaiting_profile_images(self):
return list(
ProfileImageRecord.objects.filter(
user=self.user,
status__in=[
ProfileImageStatusChoices.AWAITING,
ProfileImageStatusChoices.AWAITING_DELETION,
],
)
)
def _delete_awaiting_profile_images(
self, awaiting_profile_images: List[ProfileImageRecord]
):
for profile_image in awaiting_profile_images:
if profile_image.status == ProfileImageStatusChoices.AWAITING_DELETION:
profile_image.status = ProfileImageStatusChoices.APPROVED
elif profile_image.status == ProfileImageStatusChoices.AWAITING:
profile_image.status = ProfileImageStatusChoices.DELETED
profile_image.is_representative = False
ProfileImageRecord.objects.bulk_update(
awaiting_profile_images, ["status", "is_representative"]
)
Bad 👎
class CleanupAwaitingProfileImages:
def call(self):
awaiting_profile_images = list(
ProfileImageRecord.objects.filter(
user=self.user,
status__in=[
ProfileImageStatusChoices.AWAITING,
ProfileImageStatusChoices.AWAITING_DELETION,
],
)
)
if not awaiting_profile_images:
return
for profile_image in awaiting_profile_images:
if profile_image.status == ProfileImageStatusChoices.AWAITING_DELETION:
profile_image.status = ProfileImageStatusChoices.APPROVED
elif profile_image.status == ProfileImageStatusChoices.AWAITING:
profile_image.status = ProfileImageStatusChoices.DELETED
profile_image.is_representative = False
ProfileImageRecord.objects.bulk_update(
awaiting_profile_images, ["status", "is_representative"]
)
class CleanupAwaitingProfileImages:
def call(self):
self.execute()
def execute(self):
awaiting_profile_images = list(
ProfileImageRecord.objects.filter(
user=self.user,
status__in=[
ProfileImageStatusChoices.AWAITING,
ProfileImageStatusChoices.AWAITING_DELETION,
],
)
)
if not awaiting_profile_images:
return
for profile_image in awaiting_profile_images:
if profile_image.status == ProfileImageStatusChoices.AWAITING_DELETION:
profile_image.status = ProfileImageStatusChoices.APPROVED
elif profile_image.status == ProfileImageStatusChoices.AWAITING:
profile_image.status = ProfileImageStatusChoices.DELETED
profile_image.is_representative = False
ProfileImageRecord.objects.bulk_update(
awaiting_profile_images, ["status", "is_representative"]
)
단일 책임 원칙
각 유즈케이스 클래스는 단일 책임 원칙을 따라 하나의 특정 비즈니스 작업(사용자 생성, 인앱 상품 구매, 프로필 이미지 변경)을 수행합니다. 하나의 비즈니스 작업은 여러 개의 모델 인스턴스를 동시에 생성, 조회, 수정, 삭제하거나 외부 API 호출을 포함할 수 있습니다.
트랜잭션 관리
여러 데이터베이스 연산이 원자적으로 이루어져야 하는 경우, @transaction.atomic 데코레이터를 사용해 유즈케이스 내에서 트랜잭션을 관리합니다.
class ChangeRepresentativeImage(UseCase[ProfileImageRecord]):
def __init__(self, user: User, selected_profile_image_id: int):
self.user = user
self.selected_profile_image_id = selected_profile_image_id
@transaction.atomic
def _execute(self) -> ProfileImageRecord:
selected_image = self._get_selected_profile_image_record()
current_image = self._get_current_representative_image_record()
self._validate_approval(selected_profile_image_record)
changed_image = self._change_representative_image(
current=current_image,
next=selected_image
)
return changed_image
# ...