39

I am trying to create a backend with Django Rest Framework and am trying to determine where to place the business logic. Would it go in the views.py? I would like to create more complex services than just getting a list of objects or grabbing one specific object. Any guidance would be appreciated, thanks. I realize there is a discussion about the business logic in a generic Django project but I am asking specifically about the django rest framework.

perp
  • 477
  • 1
  • 4
  • 6
  • 1
    There is already a big discussion about this: http://stackoverflow.com/questions/12578908/separation-of-business-logic-and-data-access-in-django – Spencer May 12 '15 at 17:30
  • 2
    Yeah I looked at that one but was hoping to ask specifically about the location within django rest framework – perp May 12 '15 at 17:49
  • Did you find a good Design Pattern for this one? Thanks – jhagege Aug 19 '15 at 06:51

3 Answers3

33

It is more about design patterns rather than Django Rest Framework.

Here are some tips:

  • Providing interfaces using REST should not involve any specific code related to data manipulation or business logic.
  • Using an MVC approach does not mean that you shouldn't layer your application.
  • You should be able to test your business logic without touching the UI at all.
  • Some people may suggest putting business logic in models. But I do not agree with them, since Django models are different from domain models and business related tasks such as tax calculation.
  • Before getting stuck in MVC, You could read more about The MVC implemented in MVC three-tier architecture
  • I suggest having a business layer and related apps putting your business logic there.

MVC + three-tier diagram

Suppose that you have an online coffee shop & you'd like to provide a REST API for ordering coffees.

Here are my suggested code samples:

myapp/views.py:

    def order(request, quantity=1):
        # Process the order by calling the mapped method
        order_id = CoffeeShopService.place_order(quantity)
        return HttpResponse({'order_id': order_id, mimetype='application/json')

myapp/services.py:

    class CoffeeShopService(object):
        @staticmethod
        def place_order(quantity):
           # do the business logic here
           return order_id
Saeed
  • 661
  • 6
  • 12
  • Thank you for the insight. But I have another issue by implementing your idea. I'm difficult to standardize my exception handling. As you know in DRF we have `ValidationError`. In viewsets has another exception also in lower lever such us in models has a lot of exception class. The problem is those error handling has different format in API response. – Adiyat Mubarak Jul 14 '18 at 12:16
  • 2
    Exception handling in DRF is a different question which is possible by Custom exception handling. if you search you can find how to or you may ask a separate question – Saeed Jul 15 '18 at 18:33
  • I've started to code in `Python` with `Django` recently, before I've coded in `Java`/`Spring Boot`. I very agree with your point that declines putting logic inside the `models.py` making it even fatter. And the `services` module is the best place to make some business processes in my opinion. I just get it from `Spring Boot` experience – catscoolzhyk Dec 16 '20 at 15:50
2

It is a design patterns Questing in Rest Framework, I guess. Here is a Detailed Overview of how i use Layered Approach in my API build on Rest Framework!

It is a little more layered for the sake of Easy Maintenance and utilizes Design Patterns and GRASP Principal most importantly!

Layered Approach Package Level View

enter image description here

Further Classification:

enter image description here enter image description here

Now an Example of How I go through Layers:

  1. A Request is made to example.com/Customer/profile @project/urls.py enter image description here

  2. The Request is forwarded to the Respective URL's Layer (@app/urls/Customer_Url) The Request is forwarded to the Respective URL's Layer

  3. URL's Pass it to the Respective Viewset (@app/Viewsets/Customer_Viewsets/Customer_Signup.py) enter image description here

  4. It being a Post Request, (I assume for this Example) is Forwarded to Business Logic Layer (@app/BusinessLogicLayer/BLL.py) enter image description here

  5. The Business Logic Layer has an Abstract Implementation(Act as Interface of IBLL) and it forwards the request to respective method to check for all my Business Rules! (@app/BusinessLogicLayer/SUB_BLL/Customer/*) enter image description here

  6. The Request is then Forwarded to the Data Access Layer which stores the Data of User in Database. and So forth! (@app/DataAccessLayer/DAL.py)

Syed Faizan
  • 958
  • 9
  • 19
  • 2
    `Customer_Name is ""` -- this is plain wrong. And generally the code smells. You should use validation frameworks (for example Marshmallow). – warvariuc Aug 03 '18 at 07:55
  • 1
    I am basically Setting an Empty String for Handling some of my Custom Things, This API is interacted by a Dozens of Devices! Also this Code is the Work of my Team Member so he better Justify it, My Main goal is to give the overview of Layered Archietecture. i would myself use if not Customer_Name than that :) – Syed Faizan Aug 03 '18 at 08:02
  • 1
    @warvariuc I would really appreciate if you can pick Mistakes in the Archietecture Side as I myself is more focused on using this for a long term, so I would really love to fix my Errors in Archietecture. Most of All, Thanks Alot :) – Syed Faizan Aug 03 '18 at 08:03
  • 1
    It's difficult to judge about architecture when camel case names are used, repetitive code, wrong low level idioms. Also there is too much info and screenshots instead of text. IMO. Also I see just code and explanations but no description of the design principles used in the code. – warvariuc Aug 03 '18 at 08:14
  • 1
    @warvariuc Currently, I am studying Student with just 1 year completed in Software Engineering and have 3 more years to go through :). I support myself till this level :) and would like to learn and Grow more from your knowledge if we can communicate via Email or Skype? – Syed Faizan Aug 03 '18 at 08:17
  • Yes. You can find me on GitHub. – warvariuc Aug 03 '18 at 08:29
  • This is Great Archietecture, It organizes Stuff. Love the Tier but do you have any idea on Onion Archietecture? – Ibtihaj Uddin Oct 17 '18 at 08:58
-4

maybe this is a slightly eccentric way, but I think it's quite helpful to wrap the logic into the serializer by adding methods in it.

For example

serializer:

class OrderSerializer(serializers.ModelSerializer):
    class Meta:
        model = Order
        fields = (
            'id',
            'total',
            'discount',
        )

    def calculate_discount(self, whatever_params):
        # calculate discount if you need... and return it

    def calculate_tax(self, whatever_params):
        # calculate tax amount if you need...and return it

    def calculate_grand_total(self, whatever_params):
        # calculate grand total if you need and return it

    def create(self, validated_data):
        # You can make an order by applying 
        # some logic in the calculation method.
        # Maybe by adding a bit of the context 
        # you sent as parameters from view.
Yanwar Sky
  • 751
  • 7
  • 10
  • 2
    Even if it possible to do it, I think putting logic into `serializer`s is not obvious and wrong. Since the main idea of serializers is simply to serialize and deserialize data and make some validation staff. It is incorrect in terms of `django` project architecture. – catscoolzhyk Dec 16 '20 at 15:44