How To Build Scalable Systems with Django Framework
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.
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:
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.
Explore our blog for more Django Framework content:
A computer scientist who loves to study new technologies. Also enjoys rap, watching movies and TV shows, sports (especially soccer), and playing videogames.