Files
Deal/docs/architecture/2026-09-09-unified-card.md
T
Rustam Khalimov 27c7831910
ci / build-test (push) Canceled after 0s
Deal — единая кодовая база
SaaS-мониторинг Telegram: ядро (модули Cards/Kanban/Pipeline/Tenants/Settings/
Discovery, Api, Infrastructure), сервисы telegram/ai/ml/storage, фронт Vue,
контракты и grpc-hosting, деплой-конфиги (dev/prod/observability/CI-раннер),
Gitea Actions CI, документация (ТЗ, техдок, api-map, код-стайл, планы, бэклог).

Текущее состояние: все этапы роадмапа 0–12 закрыты, сборка 5 sln 0/0,
тесты 1340/130/52/38/9 зелёные.
2026-09-11 23:56:47 +03:00

6.6 KiB

Дейл — единая модель карточки (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#)

Ядро

/// Единственное, что есть у любой карточки.
public interface ICard
{
    string Id { get; }
    string Title { get; }
    ISource Source { get; }        // откуда пришла (см. ниже)
}

/// Типизированная проекция для сценариев, которым нужен конкретный источник.
public interface ICard<TSource> : 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).

Вид карточки = композиция модулей, не класс-наследник. Новый дашборд/вид — новая композиция + при необходимости новый модуль.

Контейнеры (общая база колонок/стадий/зон)

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, «третьи» дашборды (архитектура готова), разовые миграции.