Найти в Дзене
NeuroNest

Как правильно смоделировать сущность User по принципам Clean Architecture, чтобы не было стыдно перед будущим собой

Окей, ты решил делать проект «по красоте» — никакого г0внокода, всё по заветам дядюшки Боба. Начинаешь с фундамента — с модели User. Звучит просто, но потом ты такой: «Так, а где хранить пароль?», «А статус верификации — это атрибут или уже отдельный use-case?», «А это вообще в Entity или в UseCase?» — и понеслась. Если коротко — моделирование сущности User по принципам Clean Architecture похоже на то, как будто ты пересобираешь свой гардероб: выкидываешь всё лишнее, оставляешь только базу, а остальное — по случаям жизни. Никакой мешанины и бардака. Давай разложим всё по полочкам. Сначала — не спеши, присядь на корточки и подумай, что такое сущность В Clean Architecture сущность (Entity) — это бизнес-объект. То есть — штука, которая живёт независимо от базы данных, фреймворка, API и твоей любимой ORM-ки. Ей вообще пофигу, откуда она берётся и куда уходит. Она должна быть такой, чтобы ты мог взять её из проекта, кинуть в другой — и она бы всё ещё работала. И вот тут ловушка: ты хочешь с

Окей, ты решил делать проект «по красоте» — никакого г0внокода, всё по заветам дядюшки Боба. Начинаешь с фундамента — с модели User. Звучит просто, но потом ты такой: «Так, а где хранить пароль?», «А статус верификации — это атрибут или уже отдельный use-case?», «А это вообще в Entity или в UseCase?» — и понеслась.

Если коротко — моделирование сущности User по принципам Clean Architecture похоже на то, как будто ты пересобираешь свой гардероб: выкидываешь всё лишнее, оставляешь только базу, а остальное — по случаям жизни. Никакой мешанины и бардака. Давай разложим всё по полочкам.

Сначала — не спеши, присядь на корточки и подумай, что такое сущность

В Clean Architecture сущность (Entity) — это бизнес-объект. То есть — штука, которая живёт независимо от базы данных, фреймворка, API и твоей любимой ORM-ки. Ей вообще пофигу, откуда она берётся и куда уходит. Она должна быть такой, чтобы ты мог взять её из проекта, кинуть в другой — и она бы всё ещё работала.

И вот тут ловушка: ты хочешь сразу впихнуть в User всё — и e-mail, и refresh token, и любимую тему в интерфейсе. А по факту, половина из этого — это или детали реализации, или вообще не его забота.

Что должно быть в User?

Ну смотри. Базовая сущность User — это, по сути, бизнес-модель твоего пользователя. Кто он? Что делает? Как взаимодействует с системой? Если ты делаешь, скажем, платформу для онлайн-обучения — тебе важны: id, имя, роль (преподаватель, студент, модератор), статус (активный, заблокирован, ожидает подтверждения).

Вот так примерно:

class User:

   def __init__(self, user_id, name, role, status):

     self.id = user_id

     self.name = name

     self.role = role

     self.status = status

   def is_active(self):

     return self.status == "active"

   def can_create_course(self):

     return self.role == "instructor"

Видишь? Всё по делу. И никаких тебе лишних атрибутов, завязанных на реализацию.

А куда девать пароль?

Вот тут начинается мясо. Пароль — это инфраструктура. То есть, в бизнес-логике ты не должен знать, как он хэшируется, где он хранится, и каким образом сравнивается. Ты можешь максимум иметь некую “credential”-обёртку, с которой будешь работать абстрактно.

Некоторые вообще выносят Authentication в отдельный use-case. Почему? Потому что это — операция, а не часть сущности. Авторизация, регистрация, смена пароля — это уже процессы, а не просто данные.

Поэтому пароль — не в User, а где-то в authentication service или user repository.

И где его хранить?

Всё, что связано с хранением, уходит в infrastructure слой. Там могут быть DTO, ORM-модели, адаптеры к БД. И они мапятся на твои сущности.

Твоя UserEntity вообще не должна знать, что где-то в Postgres она лежит в таблице auth_users. Её это не касается. Ты абстрагируешься. Пусть этим занимается репозиторий или mapper.

А если нужно больше полей?

Окей, ты хочешь, чтобы в твоём User было и фото профиля, и описание, и ссылки на соцсети. Нормально. Только не запихивай это в базовую сущность. Сделай value object UserProfile, и пусть он будет вложен внутрь.

class UserProfile:

   def __init__(self, bio, photo_url, social_links):

     self.bio = bio

     self.photo_url = photo_url

     self.social_links = social_links

Таким образом, User останется чистым, а дополнительная инфа — модульной. Хочешь использовать — подключил. Нет — и не мешает.

Окей, а где логика?

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

Хочешь определить, можно ли пользователю менять имя? Сделай метод в User. Хочешь проверить, может ли он запостить комментарий? Тоже туда.

Но! Никакой логики по работе с базой, с сетью, с UI — вообще ничего такого. Только чистая бизнес-логика. Остальное — в отдельных слоях: use-cases, gateways, интерфейсы.

Почему это важно?

Потому что, когда ты начнёшь расширять проект, переделывать его под мобильное приложение, web, desktop или вообще микросервис — у тебя будет основа, которая работает везде одинаково. Без зависимости от того, какая там база, какой фреймворк и в каком формате приходят запросы.

Clean Architecture — это как раз про это: чтобы твоя логика была вне времени, места и внешних условий. А User— это твой первый кирпич.

-2

И да, не пытайся быть умнее, чем надо

Многие новички начинают лепить в User всё подряд: last_login, session_token, is_email_verified, auth_provider, ip_address, browser_info. Стоп. Смотри на User не как на базу данных, а как на персонажа в истории.

Что он делает? Какую роль играет? Какие правила поведения у него есть? Вот на это и ориентируйся.

Ну что, друг, а как ты обычно моделируешь User в своём проекте? Прям по красоте или по-старинке — “как получится”? Пиши, обсудим. А может, у тебя вообще свой подход?