본문 바로가기

소프트웨어-이야기/프로그래밍 언어와 프레임워크

(Django) Django에서 비즈니스 로직 관리하기

Django와 Ruby On Rails를 사용하면서, 항상 고민되는 점이 있다. 

각 프레임워크에서 비즈니스 로직을 모아두고, 관리하기에 적합한 위치가 어디인가에 대한 점이다. 

Rails 3/4 버전을 사용하면서, 찾아봤던 글은 레일즈에 Service/Decorator Layer 적용하기에 정리했었다.

이번에는 Django에서 비즈니스 로직을 관리하는 방법에 대해서 정리해보고자 한다.

 

Django에서 비즈니스 로직을 추가할만한 곳은 크게 4가지이다. 그리고 이들 모두 각각 장/단점을 갖고 있다.

  1. model 
  2. view
  3. service 
  4. queryset / manager

 

1. Model

Django에서 제안하는 비즈니스로직 관리 방식은 Model에 기능을 추가하는 것이다.

MVC의 기본 설계 패턴은 Fat Model, Thin Controller이다. Model에 기능을 모아두고, Controller은 중개역할만 하는거다.

이 경우, 코드의 중복이 줄어들고, 객체지향적으로 코드를 설계하기 편하다는 장점이 있다. 

 

모델에 함수를 추가하는 방식은 다음과 같다. 

class Post(Model):
   STATUS_DRAFT, STATUS_APPROVED, STATUS_PUBLISHED = range(3)
   STATUS_CHOICES = (
       (STATUS_DRAFT, _('Draft')),
       (STATUS_APPROVED, _('Approved')),
       (STATUS_PUBLISHED, _('Published'))
   )

   title = models.CharField(max_length=255)

   text = models.TextField()

   status = models.PositiveSmallIntegerField(
       choices=STATUS_CHOICES,
       default=STATUS_DRAFT
   )

   def approve(self):
       self.status = Post.STATUS_APPROVED
       self.save()

   def publish(self):
       if self.status != Post.STATUS_APPROVED:
           raise PostInvalidStatusError()
       self.status = Post.STATUS_APPROVED
       self.save()

그러나 모델에 비즈니스 로직이 많아지고, 다양한 도메인이 섞이게 되면, 시스템 규모가 커질수록 관리하기 어려워진다.

위와 같은 문제는 Django Proxy Model을 사용하면 해소할 수 있다.

 

2. View

View / Form에 복잡성이 높은 비즈니스 로직을 추가하는 것은 일단 피해야한다. 

특정 API에 기능이 종속되는 경우, 다른 API이나, 배치잡에서 해당 기능을 재사용하기 어렵기 때문이다. 

Where to Put Business Logic in Django 글에서는 그냥 이런 행동은 하지말라고 제안하고 있다. 

 

3. Service Layer

Service Layer는 모델과 뷰 사이에 비즈니스 로직을 담당하는 개념을 추가하는 것을 의미한다.

Service Layer가 추가되면, 모델에는 객체의 속성만 관리하게 된다.

 

예시 코드는 다음과 같다.

def approve_post(post):
   post.status = Post.STATUS_APPROVED
   post.save()

def publish_post(post):
   if post.status != Post.STATUS_APPROVED:
       raise PostInvalidStatusError()
   post.status = Post.STATUS_APPROVED
   post.save()

Django에는 Service Layer에 대한 명확한 기준이 없다.

Django에 Service Layer를 두는 몇가지 방식은 다음과 같다.

1. Form / Serializer가 Service Layer를 대신한다.

2. utils 모듈에 비즈니스 로직을 추가해서 사용한다. 

3. service 모듈에 비즈니스 로직을 추가해서 사용한다.

 

4. queryset / manager

모델에 쿼리를 호출하는 함수가 포함되어 있는 경우, 대량 처리가 필요한 경우 최적화되지 않은 쿼리가 호출될 가능성이 높다. 

예를 들어, 여러개의 포스트의 상태를 변경해야하는 경우, 로직이 모델에 종속되어있으면 포스트의 갯수만큼 변경 쿼리가 호출되게 된다.

그러나 QuerySet을 사용하면, 한번의 update 쿼리로 여러개의 객체의 상태를 변경할 수 있다.

때문에 QuerySet에 비즈니스 로직을 모아두면 Batch, Service Object에서 기능을 재사용하기 편하다.

class PostQuerySet(QuerySet):
   def approve(self):
       return self.update(status=Post.STATUS_APPROVED)

   def publish(self):
       return self.filter(status=Post.STATUS_APPROVED).update(
           status=Post.STATUS_PUBLISHED)


class Post(Model):
   ...

   objects = PostQuerySet.as_manager()

 

QuerySet / Manager를 사용하게 되면 크게 4가지 장점이 있다.

  1. 유닛 테스트가 쉬워진다. 
  2. 가독성이 높아진다.
  3. 코드가 간결해진다.  
  4. 쿼리 최적화가 가능하다.

 

참고

Where to Put Business Logic in Django

Separation of business logic and data access in django