Как архивировать внешние сети и модели Django?

Я создаю приложение django backend и у меня есть несколько различных моделей в моем приложении. Одна из этих моделей - Driver, и я хочу сделать следующее при вызове конечной точки create-driver:

  1. создайте драйвер в базе данных
  2. создайте учетную запись пользователя для водителя в моем каталоге B2C
  3. отправить письмо водителю об успешном создании
  4. отправить уведомление администраторам об успешном создании

Операции 1 и 2 должны либо завершиться успешно, либо провалиться вместе (атомарная транзакция).

Изначально я обрабатывал 1 и 2 в методе Driver модели save следующим образом:

Class Driver(models.Model):
  #...

  def save(self, *args, **kwargs):
      if self.pk is None:
        # Creating a new driver
          if self.b2c_id is None:
              graph_client = MicrosoftGraph()
              try:
                  with transaction.atomic():
                      user_info = B2CUserInfoSchema(
                          display_name=f"{self.first_name} {self.last_name}",
                          first_name=self.first_name,
                          last_name=self.last_name,
                          email=self.email,
                          phone_number=self.phone,
                          custom_attributes={
                              "Role": UserRole.DRIVER.value,
                          },
                      )
                      res = graph_client.create_user(user_info) # create B2C user
                      self.b2c_id = res.id
                      super().save(*args, **kwargs) # create driver in database
              except Exception as e:
                  raise e
      else:
          # Updating an existing driver
          super().save(*args, **kwargs)```

Это прекрасно работало, но мне не нравилось смешивать обязанности и добавлять логику создания пользователей B2C в метод save моего Driver. Мне нравится сохранять метод save простым и сосредоточенным на создании записи в базе данных.

Я попробовал обновить архитектуру и начал использовать контроллеры и диспетчеры событий, чтобы справиться с этим. Теперь моя архитектура выглядит следующим образом:

class Driver(models.Model):
    # ...

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.domain_events = []

    def save(self, *args, **kwargs):
        if self.pk is None:
            self.domain_events.append(EntityEvent(self, EntityEventType.CREATE))
        else:
            self.domain_events.append(EntityEvent(self, EntityEventType.UPDATE))
        super().save(*args, **kwargs)



class EntityEventType(Enum):
    CREATE = "create"
    UPDATE = "update"


class EntityEvent:
    def __init__(self, db_entity: models.Model, event_type: EntityEventType):
        self.db_entity = db_entity
        self.event_type = event_type


class EntityEventDispatcher:
    def __init__(self, b2c_entity_service: B2CEntityService):
        self._b2c_entity_service = b2c_entity_service

    def dispatch(self, events: list[EntityEvent]):
        for event in events:
            match event.event_type:
                case EntityEventType.CREATE:
                    self._b2c_entity_service.create_entity(event.db_entity)
                case EntityEventType.UPDATE:
                    self._b2c_entity_service.update_entity(event.db_entity)

class EntityController:
    def __init__(self, db_entity: models.Model):
        self._db_entity = db_entity
        self._event_dispatcher = EntityEventDispatcher(
            B2CEntityFactory.from_entity_type(type(db_entity))
        )

    def _dispatch_events(self):
        self._event_dispatcher.dispatch(self._db_entity.domain_events)

    @transaction.atomic
    def create_entity(self):
        self._db_entity.save()
        self._dispatch_events()
        return self._db_entity

    @transaction.atomic
    def update_entity(self):
        self._db_entity.save()
        self._dispatch_events()
        return self._db_entity

    @transaction.atomic
    def delete_entity(self):
        self._db_entity.delete()
        self._dispatch_events()
        return self._db_entity

class DriverController(EntityController):
    def __init__(self, driver: Driver):
        super().__init__(driver)

Как вы можете видеть, я использую диспетчеры и контроллеры, и я держу логику разделенной. Как я создаю драйверы, показано ниже:

def create_driver(request):
    data = json.loads(request.body)
    driver_controller = DriverController(Driver(**data))
    driver = driver_controller.create_entity()

Очевидно, что во втором подходе было добавлено больше кода и создано несколько классов, но преимущество, которое я получил, - это разделение логики между моделями базы данных и внешними зависимостями.

Теперь я должен иметь возможность легко добавлять внешние зависимости, такие как отправка электронной почты и уведомлений, просто добавляя события в моем DriverController и отправляя их моему диспетчеру для обработки событий.

Мне хотелось бы знать, является ли такой подход правильным или я слишком усложняю решение. Каковы плюсы и минусы этого подхода?

Спасибо

Вернуться на верх