1

I'm trying to create more separation of concerns with endpoints in my Django apps but not sure how to implement a service layer. I've read through a popular python article on this topic however it doesn't seem to answer the testing issue.

For instance, if I have a request come in to save a user and want to send an email after the user has been saved, it's popular to handle this logic by overriding on save like this:

**models.py**
class User(AbstractBaseUser, PermissionsMixin):

...

def save(self, *args, **kwargs):
        if self._state.adding:
            user.activate_user(user=self)
        super(User, self).save(*args, **kwargs)

**services.py**
from services import iNotificationService
...

def activate_user(user):
    user.active = True
    iNotificationService().send_message(user)

In the above example, iNotificationService would be an interface that the application could choose at runtime. If this user was saved in a production environment, the application would provide a class like the following

import mandrill

class EmailClient(iNotificationService)

    def send_message(user):

        message = create_message(user)

        mandrill.send_email(message)

into the services.py module so that an email was sent. But if the application was run in a testing environment, the test would mock the EmailClient by sending in an instance of the interface so that no email would actually be sent:

class iNotificationService(object)

    def send_message(user)
        pass

What I'm wondering is how I supply the instance to the services.py module so that activate_user knows which type of notification to send. My only idea around this was to pass some arg to the save method so that it would know which type of notification to use. But I'm wondering how scalable that solution would be considering the different places one might use a service.

Braden Holt
  • 1,544
  • 1
  • 18
  • 32

1 Answers1

0

If change in notification provider class only depends on environment (testing / development / production etc) you are running application in (as you have mentioned), I would suggest you to provide notification class to your application through settings. I personally use this pattern and have seen in many projects and find it very useful.

Say you have a services package which has notifications module in it containing all notification provider classes. Like this.

services
    __init__.py
    notifications.py
        iNotificationService
        EmailClient

Add notification provider class path in settings.py file.

settings.py

...
# this value can be changed / overridden based on environment.
# pragmatically or statically.
NOTIFICATION_PROVIDER = 'services.notifications.iNotificationService'
...

services/__init__.py

from django.conf import settings
from django.utils.module_loading import import_string

def get_notification_provider(*args, **kwargs):
    try:
        notification_class = import_string(settings.NOTIFICATION_PROVIDER)
    except ImportError as e:
        raise ImproperlyConfigured('Error loading notification provider: %s' % e)
    return notification_class(*args, **kwargs)

Now you can import get_notification_provider from services package and use it across your application to get an instance of notification class based on your settings. You can plug in different providers in settings based on your needs / environment.

Nafees Anwar
  • 6,324
  • 2
  • 23
  • 42