Entity / Aggregate: HotelierEntity
Дом полей сущности «Отельер» контекста chessboard. Проекции (карточка апарт-отеля в catalog,
эконом-модель доходности апартаментов в invest-analytics) ссылаются сюда и описывают только дельту.
Назначение
HotelierEntity — платформенный справочник отельеров, операторов апарт-отелей (идентификатор, название,
логотип). Проект типа ApartHotel/Hotel ссылается на отельера (Project.HotelierId, INV-PRJ-4) —
оператор показывается в карточке проекта и участвует в модели доходности (управление арендой). Справочником
управляет администратор платформы (admin API: создание, список/поиск, получение по id, изменение,
удаление, lookup); застройщикам и агентам доступны только lookup-методы.
Классификация
| Поле | Значение |
|---|---|
| Контекст | chessboard |
| Kind | Справочник (Entity — CRUD, доменной логики нет) |
| Source of truth | chessboard |
| Таблица | chessboard.hoteliers |
| Tenant isolation | Не tenanted — платформенный справочник (ADR-0052 п.3) |
Поля
| Поле | Тип | Обязательность | Ограничения (PK/FK/index/constraint) | Источник | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | PK | app | Идентификатор отельера. |
Name | string | Да | varchar(256), min 1; unique ux_hoteliers_name (lower(name)) where deleted_at is null | app (admin API) | Название оператора. Уникально среди активных (INV-HTL-1). |
LogoFileId | Guid? | Нет | без FK (внешний storage/CDN) | app (admin API) | Файл логотипа. |
CreatedAt | DateTimeOffset | Да | index ix_hoteliers_created_at | app | Дата создания. |
CreatedByUserId | Guid? | Нет | — | app | Кто создал (администратор). |
UpdatedAt | DateTimeOffset | Да | — | app | Дата изменения. |
UpdatedByUserId | Guid? | Нет | — | app | Последний изменивший. |
DeletedAt | DateTimeOffset? | Нет | partial-index filter | app | Архив — операция «удаление» выполняется как soft-delete (на отельера ссылаются проекты). |
DeletedByUserId | Guid? | Нет | — | app | Кто архивировал. |
Version | long | Да | >= 1 | app | Версия записи. |
Связи
| Связь | Тип | Ключ | Delete behavior |
|---|---|---|---|
ProjectEntity | reference (обратная) | projects.hotelier_id → hoteliers.id (FK) | restrict — архив запрещён при активных ссылающихся проектах (INV-HTL-2) |
Инварианты и переходы состояний
| Инвариант | Условие | Когда проверяется | Ошибка |
|---|---|---|---|
| INV-HTL-1 UniqueName | Name уникально среди не удалённых отельеров | при создании и переименовании | CHESSBOARD_HOTELIER_NAME_TAKEN |
| INV-HTL-2 ArchiveGuard | Архив запрещён, пока существуют не архивированные проекты с HotelierId = Id | при архивировании | CHESSBOARD_HOTELIER_HAS_PROJECTS |
Активен → архив через soft-delete. В отличие от bank.md (логическая ссылка
без FK — архив свободен), на отельера ссылаются проекты физическим FK projects.hotelier_id, поэтому
архив защищён INV-HTL-2: пока есть не архивированные проекты-владельцы, архивировать нельзя.
Жизненный цикл и операции
| Операция | Актор | Предусловие | Эффект | Отказ |
|---|---|---|---|---|
| Create | администратор | Name свободно среди активных | новая запись, Version = 1 | CHESSBOARD_HOTELIER_NAME_TAKEN (INV-HTL-1) |
| Rename | администратор | новое Name свободно среди активных (кроме себя) | Name обновлён, Version += 1 | CHESSBOARD_HOTELIER_NAME_TAKEN; конкурентный rename проигравший — тот же код (гонка сериализуется ux_hoteliers_name) |
| Update logo | администратор | запись активна | LogoFileId обновлён, Version += 1 | — |
| Archive (soft-delete) | администратор | нет не архивированных проектов с HotelierId = Id (INV-HTL-2) | DeletedAt/DeletedByUserId проставлены; исчезает из lookup и public-проекций | CHESSBOARD_HOTELIER_HAS_PROJECTS (есть ссылающиеся проекты) |
| Restore (возврат из архива) | администратор | Name снова свободно среди активных | DeletedAt = null, снова в lookup | CHESSBOARD_HOTELIER_NAME_TAKEN |
| Lookup (list/search/by-id) | застройщик / агент / public-карточка | — | активные отельеры в списках; by-id резолвит и архивные — для исторической карточки проекта, уже ссылающегося на отельера | — |
Постусловие любой мутации: Version монотонно растёт, UpdatedAt/UpdatedByUserId обновлены; событий в
шину нет (потребители читают синхронно, см. ниже).
Доменные и интеграционные события
Справочник маленький, читается lookup-API синхронно — как у bank.md. В
snapshot ProjectUpsertedKafkaEvent входит только HotelierId; название/лого отельера catalog резолвит
через lookup-API при построении витрины.
Кросс-контекстный контракт (lookup)
- Что предоставляет. По
HotelierId— параName+LogoFileIdчерез lookup-API; актуальность read-time, реплики/события нет. - Кто ожидает.
catalog— карточка проектаType in (ApartHotel, Hotel)(оператор апарт-отеля);invest-analytics— эконом-модель доходности апартаментов (оператор управления арендой). Оба хранят толькоHotelierIdи резолвят название/лого при построении проекции. - Обработка архивных. by-id резолвит и архивные (для исторической карточки уже ссылающегося проекта); в
пикерах отельера при заведении/публикации проекта архивные исключены. Архив с живыми ссылками невозможен
(INV-HTL-2), поэтому «висячих»
HotelierIdна активных проектах не возникает. - Валидация ссылки. Обязательность
HotelierIdдляType in (ApartHotel, Hotel)при публикации и существование отельера проверяются публикующей операцией проекта (INV-PRJ-4, project.md).
Индексы, хранение, безопасность
| Аспект | Значение |
|---|---|
| Индексы | PK pk_hoteliers; ux_hoteliers_name unique (lower(name)) where deleted_at is null; ix_hoteliers_created_at |
| Партиционирование / retention | none — десятки строк; retention бессрочный |
| Concurrency | оптимистическая блокировка (правка не должна затирать чужую) |
| Permissions / PII | CRUD — только admin API (chessboard.hoteliers.manage, ADR-0053); lookup — Developers/Agents API (chessboard.references.lookup) и public-проекция карточки проекта. PII нет |
| Audit | версия записи + отметки кто/когда; soft-delete |
Обратные ссылки
Автоссылки: где используется этот документ.
- Домен (CQRS) (2): Entity / Aggregate: BankEntity, Entity / Aggregate: ProjectEntity
- API (6): Admin CRUD отельеров (создание / список / карточка / правка / архив / lookup) — область admin, Chessboard API — реестр операций областей Developers и Admin, GET /api/v1/developers/lookup/{banks,hoteliers} — Lookup справочников банков/отельеров, GET /api/v1/developers/projects/{projectId} — Карточка проекта застройщика, GET /api/v1/public/projects/{projectId} — Карточка проекта (ограниченная/полная), POST /api/v1/developers/projects — Создание проекта
- Use Cases (2): UC-CHS-001. Создание и публикация проекта застройщиком, UC-CHS-007. Администратор ведёт справочники банков и отельеров
- События (1): Event Contract: pin.chessboard.project-upserted (ProjectUpsertedKafkaEvent)
- index.md (1): Chessboard Service (Pin.Chessboard)
- registries (2): Реестр инвариантов PIN, Реестр сущностей PIN
Связанные документы
- project.md — ссылка
HotelierId(INV-PRJ-4); bank.md — парный справочник. - Admin API CRUD: ../api/admin/hoteliers-crud.md; lookup: ../api/developers/references-lookup.md.