Как архивировать внешние сети и модели Django?
Я создаю приложение django backend и у меня есть несколько различных моделей в моем приложении. Одна из этих моделей - Driver
, и я хочу сделать следующее при вызове конечной точки create-driver
:
- создайте драйвер в базе данных
- создайте учетную запись пользователя для водителя в моем каталоге B2C
- отправить письмо водителю об успешном создании
- отправить уведомление администраторам об успешном создании
Операции 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
и отправляя их моему диспетчеру для обработки событий.
Мне хотелось бы знать, является ли такой подход правильным или я слишком усложняю решение. Каковы плюсы и минусы этого подхода?
Спасибо