Найти тему

Паттерн DTO в разработке

В статье про запуск скриптов на хосте из контейнера docker я затронул такой паттерн как DTO (Data Transfer Object). DTO - это объект предназначенный для транспортировки данных из одного места в другое. Кстати, когда я учился работать с питоновскими вэб фреймворками типа Джанго, Фаст АПИ и т.д. на ютюбчике, ни кото ни где не использовал такой паттерн и столкнулся я с ним только на работе, так сказать на боевых проектах. Использование такого паттерна не обязательно, но на мой взгляд довольно удобная и для общего развития не помешает

Итак, использование dto гарантирует что:

  • Транспортируемые данные не изменятся в пути;
  • Мы будем использовать только те данные которые собственно нам необходимы;
  • Ну и мы будем использовать те типы данных которые ожидаем;

Реализуют DTO (в питоне) разными способами, лично я использую NamedTuple (именованный кортеж), потому что кортеж немутабельный (да, да если поплясать и пожонглировать, то переписать кортеж вполне реально, но сейчас не об этом). Формируют DTO обычно так:

from typing import NamedTuple
class DTO(NamedTuple):
arg1: str
arg2: str
arg3: bool = None

Или так:

from dataclasses import dataclass
@dataclass(frozen=True)
class DTO:
arg1: str
arg2: str
arg3: bool = None

Такой класс не должен нести в себе ни какого поведения и отвечать только за транспортировку данных. Самый яркий пример использования DTO это передача данных с бэкэнда на фронт через сериализатор или через context. Для примера представим что у нас имеется Джанго проект:

DTO.py

class MyModelDTO(NamedTuple):
title: str
slug: str
is_active: bool = None

mdoels.py

class MyModel(mdoels.Model):
title = models.CharField('Заголовок', max_lenght=255)
slug = models.SlugField('Идентификатор', max_lenght=255, db_index=True, unique=True)
is_active = models.BooleanField('В работе', default=False)
def get_my_model_dto(self) -> MyModelDTO:
return MyModelDTO(
title = self.title,
slug = self.slug,
is_active = self.is_active
)

views.py

def get_my_mdoel(request, id):
my_mdoel = MyModel.objects.get(id=id)
context = {
'my_mdoel': my_mdoel.get_my_model_dto()
}
return render(request, template, context)

В итоге на фронт придут только значения полей title, slug и is_active. Удобненько, не надо танцевать с value / value_list и т.д. Думаю не стоит говорить что через DTO можно не только забрать, но и положить что-то в БД.

В DTO можно упаковать не только что-то одно, типа строку или инт, но и списки, кортежи и т.д.

P.S. Подписывайтесь, комментируйте, критикуйте )))