Backend System Design for Coupon Management System

 

Designing a backend for a complex system like a Coupon Management System (CMS) requires careful consideration of various components, including scalability, security, performance, and maintainability. In this blog post, we will walk through the design of the backend system for our Coupon Management System, developed using Django and Django REST Framework (DRF). The goal is to explain the fundamental decisions made during system design, the architecture choices, and the tools used to implement the backend efficiently and securely.

Overview of the Coupon Management System (CMS)

The Coupon Management System enables Veterinary Service Officer (VSOs) to track coupons collected from doctors, manage doctor points, and facilitate the redemption of these points for products or gifts. The system supports hierarchical management (from top management to VSOs), and it needs to maintain real-time data, transactional integrity, and security.

Key features of the system include:

  • Tracking Coupons: Managing coupon issuance, status, and redemption.
  • Points Management: Tracking points accrued and redeemed by doctors.
  • Product Redemption: Managing product catalog for coupon redemptions and associating products with specific point values.
  • User Management: Authenticated access for various roles like managers, VSOs, and doctors.
  • Reporting & Analytics: Offering reports on coupon activity, redemption rates, and VSO performance.

With these requirements in mind, the backend was designed to handle business logic, ensure security, provide robust API endpoints, and scale with increasing demand.

Backend Architecture

1. Tech Stack

For the backend, we used the following technology stack:

  • Django: A powerful Python-based web framework used for building scalable and secure web applications. It provided us with built-in tools for rapid development, including authentication, ORM (Object-Relational Mapping), and admin interface.
  • Django REST Framework (DRF): A powerful toolkit that allows us to build and manage RESTful APIs. It simplifies the creation of API views, serializers, and authentication mechanisms.
  • PostgreSQL: A robust relational database that supports complex queries and transactions. PostgreSQL was chosen for its stability and scalability.
  • JWT (JSON Web Tokens): Used for authentication and securing API endpoints.

2. System Design Principles

When designing the backend, we followed key system design principles to ensure that it met the business requirements effectively:

  • Modularity: Each component (e.g., coupon management, user authentication) was designed as an isolated module to ensure maintainability and ease of scaling.
  • Separation of Concerns: We separated the data layer (models), business logic (views and serializers), and presentation layer (API responses) to ensure that each part of the system is easily maintainable and extendable.
  • Security: Given that the system handles sensitive data (e.g., user information, transaction data), security measures such as JWT-based authentication, data validation, and permissions were implemented.

Database Schema Design

A critical part of the system design was creating an efficient and scalable database schema. The following key tables were designed to handle different aspects of the coupon management process:

  1. User Table: Stores user credentials and role-based access control (RBAC) information for VSOs, managers, and doctors.
    • Fields: id, username, email, password_hash, role, is_active, etc.
  2. Coupon Table: Stores information about the coupons issued to doctors, including the points used and the redemption status.
    • Fields: coupon_id, doctor_id, product_id, points_used, status, etc.
  3. Doctor Table: Contains doctor-specific information, including their points balance, and the associated VSOs who manage them.
    • Fields: doctor_id, name, contact_info, points_balance, assigned_vso, etc.
  4. Transaction Table: Logs all the coupon redemption transactions, linking coupons to products, and records points used in each transaction.
    • Fields: transaction_id, doctor_id, product_id, points_used, date, etc.
  5. Product Table: Tracks products available for redemption, their point value, and product details.
    • Fields: product_id, name, description, points_required, etc.
  6. Manager Table: Stores information about managers who oversee the VSOs and the regions they manage.
    • Fields: manager_id, vso_id, district, taluka, etc.

Each table was carefully designed to ensure data integrity, maintain relationships using foreign keys, and optimize query performance.

Authentication and Authorization

Since our system has different user roles (e.g., doctors, VSOs, managers), securing the application through authentication and authorization is critical.

1. JWT-based Authentication

We used JSON Web Tokens (JWT) to secure our API endpoints. When users log in (via the login API), they receive a JWT token, which they must include in the Authorization header for subsequent requests. This token is validated on the server to ensure that the user is authenticated.

2. Role-based Access Control (RBAC)

Role-based access control was implemented to ensure that only authorized users can access specific endpoints. For instance:

  • Managers can view and manage all coupons, products, and VSO performance.
  • VSOs can create and manage coupons for doctors within their assigned region.
  • Doctors can only view and redeem their own coupons.

This was implemented using DRF’s permissions classes and custom decorators. For example, a custom permission class might look like this:


from rest_framework import permissions
class IsManagerOrVSO(permissions.BasePermission):
   def has_permission(self, request, view):
        return request.user.role in ['Manager', 'VSO']

API Design Using APIView

For our RESTful API, we utilized DRF’s APIView class to handle different HTTP methods (GET, POST, PUT, DELETE). APIView provides greater flexibility and control over API requests than ModelViewSet.

Example: Coupon Creation API

 

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Coupon, Doctor
from .serializers import CouponSerializer

class CouponCreateView(APIView):
   def post(self, request, *args, **kwargs):
       doctor_id = request.data.get('doctor_id')
       points_used = request.data.get('points_used')
       doctor = Doctor.objects.get(id=doctor_id)
       if doctor.points_balance < points_used:
           return Response({"error": "Insufficient points"}, status=status.HTTP_400_BAD_REQUEST)
       coupon = Coupon.objects.create(doctor=doctor, points_used=points_used)
       doctor.points_balance -= points_used
       doctor.save()
       return Response(CouponSerializer(coupon).data, status=status.HTTP_201_CREATED)
  •  
  • The CouponCreateView handles the coupon creation logic.
  • Before creating a coupon, it checks if the doctor has enough points available. If not, it returns a 400 Bad Request error.
  • The coupon is created, and the doctor’s points balance is updated.

Example: Transaction History API

 

class TransactionHistoryView(APIView):
   def get(self, request, *args, **kwargs):
       doctor_id = kwargs.get('doctor_id')
       transactions = Transaction.objects.filter(doctor_id=doctor_id)
       serializer = TransactionSerializer(transactions, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

This endpoint retrieves a doctor’s transaction history, serializing the Transaction model data into JSON format.

Handling Background Tasks with Celery

Asynchronous tasks, like sending emails or updating coupon statuses in bulk, were handled using Celery. Celery allows us to offload these tasks to a background worker, ensuring the main application remains responsive.

For example, sending bulk emails for coupon redemptions could be done asynchronously:


from celery import shared_task
@shared_task
def send_coupon_redeemed_email(coupon_id):
   coupon = Coupon.objects.get(id=coupon_id)
   # Send email logic here...
    print(f"Sent email for coupon {coupon_id}")

Scalability and Future Growth

To ensure the system can scale with increasing users, data, and requests, several strategies were implemented:

  • Database Optimization: We optimized our database schema with indexing on frequently queried fields like doctor_id and product_id. This allows for faster lookups during coupon redemption and transaction history retrieval.
  • Caching: Redis was used for caching frequently accessed data, such as doctor points balance, to minimize database queries.
  • Horizontal Scaling: The backend is designed to be stateless, meaning multiple instances can be run behind a load balancer. This provides high availability and distributes the load evenly across servers.

Conclusion

Designing the backend for a Coupon Management System requires careful consideration of security, performance, and scalability. By using Django REST Framework, we were able to quickly build robust APIs with fine-grained control over authentication, authorization, and data processing. With scalable database design, token-based authentication, and background task handling, the system is built to handle the needs of the business today and grow as it evolves. By following best practices for system design, we ensured the backend could scale and adapt to future growth and requirements.

 

Leave a Comment

Your email address will not be published. Required fields are marked *

Shopping Cart