Package: drf-service-layer
DRF-Service-LayerSimple package which supports service-layered architecture for Django REST Framework.Why service layer?Have you ever wondered where to put your business logic when you use Django & DRF? There are several solutions with
their pros and cons. Let's check them one by one.Fat Models, Skinny ViewsThis is one of the most popular ways to split business logic from views. To keep your views light, all the heavy
codes go into "fat" models. The problem is that, as your project gets bigger, models become fatter and have too many
responsibilities. They become "god" models over "fat" ones in the end. Besides, there are some cases when your
business logic doesn't require any database access or they refers to multiple tables. In these cases, it's ambiguous which
model should manage the logic.QuerySet/ManagersIt might be preferable to move your business logic from models to queryset or managers. But still, this solution is
not a good choice like the "Fat Models approach" if your business logic doesn't need to communicate with databases.Forms or SerializersProbably the worst option. They each have their own purpose. Please don't do this.Fat ViewsIf all of your business logic stays in views, you'll have trouble understanding the flow of your views in a very
short period of time. And if you inherit one of your fat views, the parent view and the child view are too strongly
coupled. So you'll have a hard time when you want to extract the legacy apis from your project.Service layerSplit your business logic into functions and put them in a separate layer which ties models and views. To manage
functions efficiently and improve the cohesion of codes, combine them into classes. In this way, views become easier
to read and business logic becomes much more maintainable. Even though this may not be a standard design pattern from
Django convention, some big companies
likeDoordashare
already using this pattern by implementing it on their own.How to use DRF-Service-Layer in ViewStepsInstall packagepipinstalldrf-service-layerDecide a type of DTO.💁 What is DTO?DTO(Data Transfer Object)is an object that carries data between processes.In DRF-Service-Layer, DTO is an object used for transferring data necessary for your business logic.DTO works between views and the service layer. If you want to transfer any data from a view to a service, implementdto propertyin your view that inherits GenericServiceAPIView from DRF-Service-Layer. We'll cover this
shortly.Let's implement DTO. There are several container types you can choose. If you want to validate your DTO before
transfer, you can use 3rd party library likePydantic.DTO as dataclass# services.pyfromdataclassesimportdataclassfromtypingimportOptional@dataclassclassOrderDTO:user_id:intorder_id:intsort:Optional[str]=NoneDTO as dictionaryDTO as listor any type you want...Implementdto propertyin views.If you decide to use dataclass as DTO:# views.pyfromdrf_service_layer.viewsimportGenericServiceAPIViewclassOrderAPIView(GenericServiceAPIView):@propertydefdto(self)->OrderDTO:returnOrderDTO(user_id=self.request.user.id,order_id=self.kwargs['order_id'],sort=self.request.query_params.get("sort"),)Create a service class and implement business logic as an instance method.# services.pyfromdrf_service_layer.servicesimportServiceclassOrderService(Service):dto:OrderDTOdefany_business_method(self):user_id=self.dto.user_idorder_id=self.dto.order_idsort=self.dto.sort# business logic goes here.Specify a service class into a view asservice_class.# views.pyclassOrderAPIView(GenericServiceAPIView):service_class=OrderService# new@propertydefdto(self)->OrderDTO:# ...Use service layer in a view.# views.pyclassOrderAPIView(GenericServiceAPIView):service_class=OrderService@propertydefdto(self)->OrderDTO:# ...defget(self,request,*args,**kwargs):# new# ...self.service.any_business_method()# ...returnResponse(...)DescriptionWhen a view is initialized by DRF'sinitial()method,dto propertyis used as an argument for instantiating the
service layer. DTO is already injected into the service layer as an instance variable(self.dto), so you don't need to
care about parameters when implementing business logic and using them. You can get editor support from type
hinting(self.dto: OrderDTO) when you deal withself.dto. After all, you can call any method from the service layer
usingself.servicein your views.(WIP)How to use DRF-Service-Layer in Serializerfromdrf_service_layer.servicesimportservice_layer@service_layer(FooService,FooDTO)classFooSerializer(serializers.ModelSerializer):# ...deffoo(self,obj):# ...returnself.service.any_business_method()DescriptionIf you add@service_layer()decorator to your serializer, you can access the service layer throughself.serivce. The
decorator explicitly notifies you that the serializer is connected to the service layer.Inspired byHow to implement a service layer in Django + Rest Framework