Django Service Layer — To the Rescue
by Lucian Corduneanu • over 4 years ago • 5 min read
At Sensidev we are using multiple Django/Python Web Services built with Django Rest Framework for user, facility, and device management. Right now we are in the process of updating our style guide so that we can:
- decouple Django apps into encapsulated and reusable building blocks
- decouple application/business logic into a separate layer
We expect to grow our team, and we want to prepare the codebase to be flexible and easy to isolate and test, so we can move fast, break things and avoid a single point of failures.
In my search for a solution to our problems, I found a very well written article from Paul Hallett. I agree with most of the patterns he suggested in his "Pragmatic style guide for the API age".
Considering Django apps as "business domains" that have all the mechanisms for full encapsulation like interfaces
and apis
seems overkill at first, but it might help in the long term, growing to a 28+ team of devs.
We have already decoupled application/business logic into a Service Layer e.g.
class DeviceService(object):
@staticmethod
def get_device_by_pk(pk):
return Device.objects.get(id=pk)
@staticmethod
def get_devices_for_building(building):
return Device.objects.filter(room__floor__building=building)
Reusable services as a package
For us, the Service Layer is not only about data queries and coordination. We also use it for providing generic support for MQTT, MongoDB, and other integrations. Since this kind of integrations is used all over the place, within different Web Services. We ended up creating a separate python package (not a Django app) with only the reusable services we could use among different Django projects.
Note that a Django project in our architecture is a standalone Web Service that resolves a business requirement like a device management system. Not to be confused with a service within the Service Layer, which is basically a class that implements application/business logic.
Here is how a reusable service across different Django projects looks like:
class DeviceMongoDBService(BaseMongoDBService):
# MongoDB service for devices storage.
def get(self, device_eui, extra_query_dict=None, projection_dict=None):
# Get device status details for given EUI.
query_dict = {
'_id': device_eui,
}
if extra_query_dict is not None:
query_dict.update(extra_query_dict)
return self.db.find_one(query_dict, projection_dict)
def update(self, device_eui, **kwargs):
# Update or insert status details for given device by EUI.
query_dict = {
'_id': device_eui,
}
update_data = {
'$set': {**kwargs}
}
self.db.update_one(query_dict, update_data, upsert=True)
We use this service to get the device status from MongoDB, like online status and many others. Also, we can perform write operation i.e. update
keyword arguments fields for a given device.
Skinny models
We already use skinny models
with no complex functional logic, but unfortunately, we still have many database relations between Django applications, which makes refactoring a bit difficult. However, I'm not really sure yet if we should drop all the relations between Django models as Paul suggests:
Table dependencies (such as ForeignKeys) must not exist across domains (Django apps). Use a UUID field instead, and have your Services control the relationship between models.
Would it make sense to just drop Django ORM support for joining tables, and take care of this manually in the Service Layer? Hard to say. There are pros and cons. For example, you might trade ORM joining tables support for better flexibility and control, effectively isolating your Django apps.
Until further clarifications, we might consider it only between Django apps, but not within the same Django app.
Conclusion
We are actively looking to improve our Django architecture with practical and modern solutions for the "API age". Most of us indeed use Django to build API points to be consumed by the Frontend. However, we still use the Django admin interface to offer to our staff members a way to perform certain functions that won't be available for typical users in the beautiful Frontend interface.
This short post is just a discussion starter, since the topic is huge. Please let us know, in the comment section below, what are your thoughts. Also, I invite you to have a beer over a video call and chat about this. Should we maybe start a Django/Python "for the API age" Meetup?
Dev Thoughts
How to migrate a PodBean podcast website to a custom website with Nginx permanent redirects
by Lucian Corduneanu • 6 months ago• 13 min read
The Product Owner’s View: Building a Streamlined Transport Management System
by Alexandra Voinea • 7 months ago• 5 min read
Improve Your Dev Journey: Essential Skills Beyond Just Coding
by Dragos Ispas • 7 months ago• 5 min read