How To Build Scalable Systems with Django Framework

woman working app development django
Summary
  • Cheesecake Labs developed a Django Starter Kit to address recurring challenges like duplicated work and architectural inconsistency across client projects The starter kit is a production-ready, dockerized foundation featuring reusable modules, pre-configured packages, and a comprehensive styleguide to ensure consistency and scalability Its architecture separates a central core app from independent, decoupled feature apps, each following a structured pattern with views, use cases, serializers, tests, and documentation per endpoint Future plans include adding more reusable modules and evaluating Django Ninja as a potential replacement for Django REST Framework to reduce boilerplate code

At Cheesecake Labs, we work on a wide range of projects, each with unique demands and complexities. After 10 years of working with clients to build scalable digital products that help solve real-world problems, we’ve noticed some recurring themes in our work.

  • Duplicated work: Despite each project being unique, certain elements of everything we work on are boilerplate, and we repeat similar tasks from one project to the next. We could accelerate our initial deliveries and reduce project timelines with standardized setups.
  • Architectural variance: Each project had a distinct architecture and development style. Creating a base architecture and a styleguide would make it easier for developers to transition between projects.

With these challenges in mind, we decided to put together starter kits on our main stack that we can use from one project to the next. 

Let’s take a look at the details of our Django Starter Kit.

In this post: 

What is the Django Starter Kit?

The Django Starter Kit is a project in constant revision. It’s designed to contain reusable code for most of our projects in a robust and scalable architecture, ensuring each project starts out with a strong and adaptable foundation.

Our starter is more than just boilerplate code. It’s a dockerized, production-ready project containing:

  • Modules: Logic required for any project, including authentication, user management, and terms of agreement. Each module is designed to be easy to extract or extend.
  • Pre-configured packages: The best settings for Django, Django Rest framework, incorporating error monitoring, interactive API documentation, and other valuable components.
  • Styleguide: Comprehensive documentation offering detailed architectural insights, best practices, and conventions. We developed our own styleguide based on an existing styleguide.

The architecture

Our architecture is composed of two types of modules — the core app and other apps. The main goal is to ensure that all apps, besides the core app, remain independent and decoupled.

The core app

The core app serves as the backbone of our project, containing essential components that are required across the entire application. It contains various functionalities, including settings, common code, abstractions, and utilities that can be shared and used by other apps within the project.

Other Apps

All other apps are modules that can interact with the core app. They all share a similar structure that looks like this:

.
├── models
│ └── ...
├── services
│ └── ...
├── utils
│ └── ...
├── v1
│ ├── user
│ │ ├── get
│ │ │ ├── views.py
│ │ │ ├── docs.py
│ │ │ ├── serializers.py
│ │ │ ├── tests_get_user.py
│ │ │ ├── use_case.py
│ │ │ └── ...
│ │ ├── ...
│ ├── urls.py
│ └── ...
├── urls.py
├── conftest.py
└── ...Code language: Django (django)

Some files like __init__.py and migrations were omitted to keep only the most important aspects of the structure.

In our architecture, every endpoint has a folder containing the following:

  • View: Each API should contain one view. The view handles the incoming requests and returns the appropriate responses.
  • Use cases: Each API should have one use case, which should be called by the view. Use cases represent the business logic of the API.
  • Docs: Each API should have documentation specifications. The Documentation file contains the API definition and is used to generate the OpenAPI page.
  • Tests: Comprehensive test suites should be created for each API to cover various scenarios, including edge cases and error conditions.
  • Request and response serializers: Each API should have request and response serializers. Serializers help with data validation and transformation.

In scenarios where multiple use cases share similar business logic, business logic is extracted and consolidated into the services or the utils folder, ensuring a cohesive and organized codebase. Our architectural philosophy emphasizes keeping business logic within specific designated areas: use cases, models, and services/utils. 

App independence and code reusability

Keeping code readable and maintainable can be a big challenge. That’s why keeping the apps decoupled is so important. 

This is how we handle app independence in the starter kit:

  • To promote code reusability, we extract code snippets that are common across multiple apps to the core app. Of course, some code redundancy is acceptable.
  • When we need to extend a highly specific property from the core model, we use Django’s Proxy Model. This allows us to extend the core model’s functionality without altering its core behavior. Here’s an example:
from core.models import User as CoreUser

class User(CoreUser):
    def my_app_specific_property(self):
        ...

    class Meta:
        proxy = TrueCode language: Django (django)
  • When creating a relationship with a model that does not belong to its app or the core, we opt for unmanaged models. This approach lets us use the model without importing the original and avoids unnecessary dependencies.
class ModelName(BaseModel):
    essential_field = models.TextField()

    class Meta:
        managed = False
        db_table = 'original_app_model_name'

class ModelWhichNeedsRelationship(BaseModel):
    relation = models.ForeignKey(ModelName, ...)
    ...Code language: Django (django)

Future improvements

The Django starter kit has been essential in maintaining code quality and consistency across our projects. 

But our journey doesn’t end here. At Cheesecake Labs, we recognize that web and mobile development requires ongoing adaptation and innovation.

We firmly believe that while our current approach is a robust foundation, there’s no silver bullet. We remain committed to refining our architecture to meet the unique demands of each project that comes our way.

Some of the exciting features we’re planning for the future:

  • More modules: We’ve mapped more frequently used modules that should be integrated into our starter to improve our delivery time. For example, we plan to add a push notification module.
  • Framework alternatives: We are considering whether Django Ninja is a better alternative than Django REST Framework for our starter kit. This replacement could significantly reduce boilerplate code because simple typehints would replace the serializers and API specifications.

We’re excited to keep innovating and finding new ways to streamline our development and build better products for our clients. 

Learn more about how we work 

To learn more about how we work at Cheesecake Labs, check out the rest of our blog, where we cover all things software development and design and dive deep into our processes and approach.

And if you’d like expert help building delightful digital products, let’s chat! We’d love to hear from you.

build scalable applications

Explore our blog for more Django Framework content:

FAQ

What is the Django Starter Kit?

The Django Starter Kit is a production-ready, dockerized project designed by Cheesecake Labs to provide reusable code for most of their projects. It includes pre-built modules for authentication and user management, pre-configured packages, and a comprehensive styleguide with architectural best practices and conventions.

What are the two main architectural components of the Django Starter Kit?

The architecture consists of the core app and other apps. The core app serves as the backbone, containing shared settings, utilities, and abstractions. All other apps are independent, decoupled modules that can interact with the core app but maintain their own structured folders for models, services, views, use cases, serializers, tests, and documentation.

How does the Django Starter Kit ensure app independence and code reusability?

App independence is maintained by keeping apps decoupled. Common code is extracted to the core app for reusability. Django's Proxy Model is used to extend core model functionality without altering it, and unmanaged models are used when creating relationships with models outside an app's scope, avoiding unnecessary dependencies.

What is included in each API endpoint folder within the starter kit's architecture?

Each endpoint folder contains a view for handling requests and responses, a use case file for business logic, a docs file for OpenAPI documentation specifications, a tests file covering various scenarios, and request and response serializers for data validation and transformation.

What future improvements are planned for the Django Starter Kit?

Cheesecake Labs plans to add more frequently used modules such as a push notification module to improve delivery time. They are also evaluating Django Ninja as a potential alternative to Django REST Framework, which could reduce boilerplate code by replacing serializers and API specifications with simple type hints.

About the author.

Karran Besen
Karran Besen

A computer scientist who loves to study new technologies. Also enjoys rap, watching movies and TV shows, sports (especially soccer), and playing videogames.