# Дейл — единая модель карточки (unified card) > Исторический документ (дизайн этапа 9, 2026-09-09). Актуальное состояние — `docs/superpowers/STATUS.md` и `docs/technical/Техническая-документация-Дейл.md`. > Дата: 2026-09-09 > Статус: дизайн согласован с владельцем продукта (в чате), начало реализации. > Связанные документы: `docs/spec/ТЗ-дейл-новая-архитектура.md`, `docs/architecture/2026-09-05-deal-architecture-design.md`. ## Проблема Сейчас в системе **два «домена» карточек**, хотя по смыслу это одна сущность: | | Канбан (`/api/leads`) | «Выбранные» (`/api/projects`) | |---|---|---| | Таблица | `Cards` | `ProjectCards` | | Контейнер | колонка `inbox/board/archive/trash/taken` | стадия `planned…finished/rejected` | | «Взять в работу» | `col=taken` + **копия полей** в `ProjectCards` | создание второй записи | | Драйвер/карточка | `CardDto` | `ProjectCardDto` | Переход «лид → проектная карточка» — это **клонирование в другую сущность**: у карточки меняется id, теряется связность истории, третий вид карточки/дашборда потребует третьей таблицы и третьего конвейера. **Решение (согласовано):** карточка — **один агрегат** во всех дашбордах. Понятие «лид» упраздняется: сообщение из канала — это *входные данные*, из которых создаётся карточка. «Взял в работу» — это **переход карточки в другой контейнер** той же доски пространства «Выбранные», а не создание новой записи. ## Модель (C#) ### Ядро ```csharp /// Единственное, что есть у любой карточки. public interface ICard { string Id { get; } string Title { get; } ISource Source { get; } // откуда пришла (см. ниже) } /// Типизированная проекция для сценариев, которым нужен конкретный источник. public interface ICard : ICard where TSource : ISource { new TSource Source { get; } } ``` ### Источники (ISource) — иерархия, а не enum-свойство - `ISource` — общее: `DisplayName`, `OriginRef`, `RawPayload`, `ReceivedAt`. - Простые: `ILocalSource`, `IWebSource`, `IFileSource`. - Сложные: `ITelegramSource` (dialogId/messageId/peer/topic), `IRowSource` (импорт колонки/строки), `IApiSource`, `IAiSource` (провайдер+модель+агент), `ICompositeSource { Origin, Pipeline[] }`. ### Модули-роли карточки (опциональные части одного агрегата) `IContentCard` (блок «О заявке»), `IBudgetedCard`, `IContactCard`, `IAttributedCard` (стек/грейд/локация — настраиваемые атрибуты тенанта), `ICommentableCard`, `ILinkCard`, `IFileCard`, `ITzCard`, `ITraceableCard` (история), `IRemindableCard`, `ILocatedCard` (контейнер + prev + isNew). Вид карточки = композиция модулей, **не класс-наследник**. Новый дашборд/вид — новая композиция + при необходимости новый модуль. ### Контейнеры (общая база колонок/стадий/зон) ```csharp public interface IContainer { string Id { get; } string Name { get; } string Color { get; } int Order { get; } IContainerRules? Rules { get; } // фильтры попадания (пользовательские колонки) IContainerPolicy Policy { get; } // поведение (роль, не enum) } ``` Политики: возврат/очистка (корзина 7д, архив 90д), терминальность («Отклонено/Выполнено» — только ручная очистка), «выбранные не попадают в архив дашборда». Отсев пайплайна — **не карточка**, вне этой модели. ### Переходы Один `ICardMover.MoveAsync(card, toContainerId, ctx)`; правила — в политиках контейнеров и «воротах» между пространствами; побочные эффекты карточка делает через свои модули (`ITraceableCard` пишет историю, `IRemindableCard` сбрасывает напоминание, `ILocatedCard.IsNew=false`). ## Терминология - ~~лид, lead~~ → **карточка (card)**; входное сообщение → **сообщение-источник**. - ~~проектная карточка~~ → карточка в контейнерах пространства «Выбранные». - «Взять в работу» → переход в контейнер `planned`. ## Что меняется - **БД**: таблицы `Cards` + `ProjectCards` → одна `Cards` (+ модульные данные); доски и стадии — единый реестр контейнеров; удаляется `ProjectCards`, перенос `LeadComments` в модуль карточки. - **Бэк**: модули Kanban и Projects объединяются в один модуль карточки/контейнеров; порты/сервисы/адаптеры/DTO — единые. - **Pipeline**: создаёт карточку (не «лид»), кладёт в контейнер по правилам. - **API**: единый контракт `/api/cards` + `/api/containers`; `/api/leads`, `/api/projects` упраздняются (фронт переписывается). - **Фронт**: один state-слайс карточек, один рендер карточки/драйвера, один канбан-компонент. ## Границы этапа Данные тестовые — схема пересоздаётся, миграции данных нет. Вне рамок: Kafka, «третьи» дашборды (архитектура готова), разовые миграции.