[PYTHON] Django REST Framework + Clean Architecture Design Consideration

Django REST Framework + clean architecture design considerations

Introduction

With the recent rise of AI, I think that the choice to build with Python for new system development has increased. When it comes to writing new web services in Python, I think the main options are to provide a framework such as React or Vue.js for the front end and a REST API with Django or Flask for the back end. With a simple configuration, the Django REST Framework makes it easy and simple to create an API server, but when it gets a little complicated, it quickly becomes chaotic if you don't think about the architecture properly. ~~ In the case I entered, this is already chaos and chaos. .. .. ~~ As far as I've looked, there's no article on the combination of Django REST Framework + Clean Architecture, so I'll consider it in this article as well as my own study and summarizing my thoughts. ~~ It's rather painful, so I'd like to introduce it with the contents summarized here. ~~ Please comment if you have a good design.

This time, we are considering excluding Project and Application (described later). Also, routing, ViewSet, ModelManager, etc. are not considered.

Since it's just pictures and sentences, I tried to implement a sample. Sample code can be found here [https://github.com/ryoutoku/django_rest_clean_architecture). Based on the Django tutorial model, I'll just implement Question POST. (However, I couldn't think about the business logic so much, so the implementation is just making POST processing uselessly redundant.)

Django REST Framework Configuration and Issues

Django itself is a framework based on the idea that there are multiple Applications in one Project, and MVT (Model, View, Template) in it. In the Django REST Framework, there is no Template and more Serializer.

Below is a diagram of the architecture and roles of the Django REST Framework. For CRUD processing for one model (1 table of Database), basically view -- serializer -- model is 1 to 1 to 1, and it can be made simply.

base architecture.png

For business-level Web services, when REST API is executed, logic processing is performed according to some business knowledge, rules, and judgments, so it is rare that only 1table CRUD processing is performed. However, if you start implementing it without deep consideration of the architecture, the architecture provided by the Django REST Framework will lead to chaos. .. ..

For me, the following two points are big problems that tend to be chaotic. Isn't it basically a framework that easily breaks the single-responsibility principle? I feel that.

Problem 1. views tends to be complicated (prone to Fat Contoller)

This is a problem called Fat Controller, in which logic is written in views. It is a problem that tends to occur when a beginner or someone who is not interested in architecture makes it.

In Django REST Framework, if it is simple, there is almost no implementation of CRUD processing as follows.

models.py


from django.db import models

class Question(models.Model):
    #Offer ORM
    question_text = models.CharField(max_length=10)
    pub_date = models.DateTimeField('date published')

serialzers.py


from .models import Question
from rest_framework import serializers

class QuestionSerializer(serializers.ModelSerializer):
    # data <->Provides model conversion
    class Meta:
        model = Question
        fields = '__all__'
        # data <->model Define the model and field to be converted

views.py


from rest_framework import viewsets
from .models import Question
from .serializers import  QuestionSerializer

class QuestionViewSet(viewsets.ModelViewSet):
    #REST API I/Offer O
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

    #Because the inheritance source ModelViewSet defines a function that is an API for CRUD processing.
    #No need to create a method

CRUD processing is provided and hidden on the Django REST Framework side. Therefore, for example, if you have to incorporate some logic when saving data (POST), you can easily write it in views-> "Add it to views (Controller) for the time being ". Isn't it often the case that you write various things in .py?

If you don't write a new class instead of writing it solidly, it will cause you to become a Fat Controller.

Problem 2. Business knowledge is easily scattered in serializers and models

Since Serializer provides validation, you have to worry about where to implement validation (not at the level of business knowledge) such as the upper and lower limits of values and the format of strings. If you put the logic in such a way that it is implemented in serializers because validation is provided, it will put seliarizers on multiple responsibilities. What is the validation of serializers when implemented in models? It will become. Also, if you are developing with a large number of people, the logic will be scattered unless you implement it with a unified idea. .. ..

If you don't think about what to implement and where, it can cause chaos.

Causes of personal problems

I feel that the fact that Serializer provides too many functions (too good) is the reason why it tends to be chaotic. Specifically, I wonder if the cause is as follows.

-serializer.save () saves the corresponding model record (depending on the inheriting Serializer class) --Because it can be saved using either model or serializer --Serializer has validation function and you can customize validation -> I want to put business knowledge in serializer.validation () (because it's called validation)

Preliminary survey 1. Design / implementation of another person

I googled "django rest framework logic" and it came out first [this page](https://medium.com/@amarbasic4/django-rest-framework-where-to-put-business-logic-82e71c339022 In), it is written that the logic is collected in serializers. However, there is a problem with the test. This person is written as writing in views, but I feel that business logic is likely to be scattered. .. .. ..

Preliminary Survey 2. Support between Django REST Framework and Clean Architecture

Below is a diagram of the correspondence between my interpretation of Django REST Framework and Clean Architecture. The left is the original composition, the right is the [usual figure] of Clean Architecture (https://blog.cleancoder.com/uncle-bob/images/2012-08-13-the-clean-architecture/CleanArchitecture.jpg) I tried to map it. The left is the reference and the right is the mapping.

relation arch.png

Looking at this, there is no corresponding xxx Business Rule. .. ..

Django REST Framework + Clean Architecture Design Review

design

Based on the above, we designed it as follows. Or rather, I probably referred to here.

new architecture.png

Roughly, I designed it with the following policy. Obviously, domain logic is not written in views, serializers, and models. Conversely, if it doesn't include domain logic, it's supposed to be used according to the Django REST Framework.

behavior

The behavior when the REST API is hit is described below (some are not implemented).

--When executing Query (GET)

--Use the functionality provided by the Django REST Framework --If you are pulling data from a resource other than Django, define it on the serializer side with SerializerMethodField etc.

--When executing Command: No business logic ver

--In validate () of serializer, validate the value using ʻAggregate and DomainObject`

--When executing Command (POST / PUT / PATCH / DELETE): With business logic ver

  1. views passes dict of data from serializer to ʻapplication_services` as an argument
  2. ʻapplication_service creates ʻAggregate based on the argument dict and processes it. --Use ʻIXXXReader to generate ʻAggregate from DB data --When saving the data, based on the argument dict, generate ʻAggregate, pass it to ʻIXXXWriter and save it. --Returns a response-aware dict to views
  3. Return using serializer for response based on dict obtained from ʻapplication_services`

Summary and impressions

Below is a summary of what I thought while researching and designing.

Regarding implementation

--I think that the I / F on the Interface Adapters side and the XXX Bussiness Rules side was simple and the logic could be divided. --But there are still more files and modules ――It may be unpopular with people who care about performance --The habit of data and validated_data (especially DateTimeField) of serializer is strong. --I'm addicted to returning str for data and datetime for validated_data. --I want to make it a little easier to implement, such as DomainObject, or make it simpler. --It is better to use NamedTuple or to dict the property to make it easier to make and use. --Depending on the contents of Query, Command, and Command, there are implementation patterns only for REST Framework and implementation patterns that consider Clean Architecture. --Patterns tend to be chaotic, so common recognition is required within the project --In Python, defining Interface is not very good.

Regarding actual operation

If you want to create an API server (or rather design a URI), I thought it would be better to use GraphQL and REST API properly with the following policy.

--Provided by GraphQL for Query --From a UI-based perspective, it is difficult for REST APIs to provide only the necessary data, and if you try to provide REST APIs for each UI, the number of APIs will increase. --Since Query does not require validation, GraphQL, which can be provided flexibly, may be more suitable. --Provided by REST API for Command --Since it seems that business logic is often involved in each command, it is better to explicitly determine the API and its parameters instead of providing flexible I / F.

Finally

I'm sure at the current site "It's simpler and easier to understand if you stuff the logic in views (1 method 200 lines or more logic implemented) " "If you make a class in vain, the performance will be ... (It seems that performance is not measured)" Ah ... I don't understand ...

References

https://medium.com/@amarbasic4/django-rest-framework-where-to-put-business-logic-82e71c339022 https://isseisuzuki.com/it/django-rest-framework-concept/ https://qiita.com/MinoDriven/items/3c7db287e2c66f36589a https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html https://blog.tai2.net/the_clean_architecture.html

Recommended Posts

Django REST Framework + Clean Architecture Design Consideration
Django REST framework basics
Django Rest Framework Tips
Django REST framework stumbling block
Django REST framework with Vue.js
Login with django rest framework
[Django] Use MessagePack with Django REST framework
Logical deletion in Django, DRF (Django REST Framework)
Understand the benefits of the Django Rest Framework
CRUD GET with Nuxt & Django REST Framework ②
Miscellaneous notes about the Django REST framework
Django REST framework A little useful to know.
Implement JWT login functionality in Django REST framework
Implementing authentication in Django REST Framework with djoser
Create a Todo app with Django REST Framework + Angular
More new user authentication methods with Django REST Framework
Create a Todo app with the Django REST framework
Create APIs around user authentication with Django REST Framework
When you want to filter with Django REST framework
List method for nested resources in Django REST framework
Implement APIs at explosive speed using Django REST Framework
[Django Rest Framework] Customize the filter function using Django-Filter
Implement hierarchical URLs with drf-nested-routers in Django REST framework
Django python web framework
How to write custom validations in the Django REST Framework
How to reset password via API using Django rest framework
Django rest framework decorators ʻaction decorator replaces list_route and detail_route`