⟨/⟩ Nts/35_ChangeRequests/DOC_CR_2026_11_03_SelectedModificationsBaseEntity.cs

122 строк · в начало

using Rugrid.Sdaid.Notation.Documents;

namespace Rugrid.Sdaid.Epas.Nts.ChangeRequests
{
    /// <summary>
    /// `SelectedModifications` приведена к общей инфраструктурной конвенции сущностей модуля
    /// (`BaseEntity`, суррогатный `Id`) — была единственной сущностью без идентификатора, с
    /// композитным ключом из двух FK. Уникальность пары (расчёт, модификация), которую раньше
    /// давал сам композитный ключ, зафиксирована отдельно — без этого гарантия потерялась бы.
    /// </summary>
    public class DOC_CR_2026_11_03_SelectedModificationsBaseEntity : IChangeRequestDocument, IHasStructuralLinks, IFolder<NtsChangeRequestsFolder>
    {
        public SdaidLink[] Links => new[]
        {
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_SelectedModifications>(),
        };

        public static string _refName = "CR 2026-11-03: SelectedModifications приведена к BaseEntity";

        public string Name => "SelectedModifications приведена к BaseEntity";

        public string Description =>
            @"SelectedModifications получает суррогатный Id — как и все остальные сущности модуля (общая инфраструктурная конвенция BaseEntity), вместо композитного ключа из двух FK. Уникальность пары (VehicleCalc, VehicleModification), которую раньше обеспечивал сам композитный ключ, зафиксирована отдельно.";

        public string Version => "0.2";
        public string Status => "done";
        public string[] Comments => new string[0];

        public static string Source = """
            ## Источник (как есть)

            `SelectedModifications` была единственной доменной сущностью модуля без `Id` —
            явное архитектурное решение («строка хранит пару ключей, отдельного идентификатора и
            собственных атрибутов у связи нет»), композитный первичный ключ из двух FK
            (`VehicleCalc`, `VehicleModification`).

            Реальная инфраструктура (`src/Common/Abstracts/BaseEntity.cs`,
            `BaseEntityConfiguration<TEntity> where TEntity : BaseEntity`) устроена иначе и жёстче:
            `Id` — всегда суррогатный первичный ключ (`builder.HasKey(c => c.Id)`), плюс пять
            технических колонок (`CreatedDate`/`CreatedBy`/`ModifiedDate`/`ModifiedBy`/`xmin`),
            которые достаются любой сущности через этот базовый класс автоматически. Проверено по
            всему `Ntr.Domain` (26 сущностей через `BaseEntity`, плюс справочники через
            `BaseReferenceEntity`/`BaseEnumEntity`) — ни одной сущности-связи без собственного
            `Id` не найдено; альтернативной инфраструктуры под композitный ключ в проекте нет.
            `Nts.Domain` (персистентный слой ЭНТС) пока не существует вовсе — для ЭНТС есть
            только `Nts.Calc` (калькулятор).
            """;

        public static string Analysis = $"""
            ## Разбор

            1. **`Id` — техническое решение, не бизнес-атрибут связи.**
               {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Domain.DOC_SelectedModifications>()}
               по-прежнему не имеет собственных БИЗНЕС-атрибутов — только пара ссылок на `VehicleCalc`
               и `VehicleModification`; добавленный `Id` не меняет это, он — следствие общей
               инфраструктурной конвенции, которой подчиняются все остальные сущности модуля.
            2. **Добавление `Id` само по себе — не полное решение.** Композитный ключ бесплатно
               гарантировал: одна и та же модификация не может быть отобрана для одного расчёта
               дважды. При замене на суррогатный `Id` эта гарантия не сохраняется автоматически —
               нужна отдельно заданная уникальность пары (`VehicleCalc`, `VehicleModification`).
               Это зафиксировано явно в документе («Идентификатор и уникальность»), а не оставлено
               подразумеваемым.
            3. **Не предмет этого CR:** собственно БД-уровень (уникальный индекс в
               `Nts.Domain`/EF-конфигурации) — реализовать нечего, этого слоя для ЭНТС ещё нет;
               зафиксировано как требование к будущей реализации, не как код.
            """;

        public static string BlastRadius = $"""
            ## Blast radius

            ### Спецификация

            | Документ | Правка |
            |---|---|
            | `Nts/50_Domain/SelectedModifications.cs` | класс-doc и `Purpose` — убрано «нет идентификатора», добавлен раздел «Идентификатор и уникальность»; сущность `SelectedModifications` — добавлено поле `Id`; `Description` у `VehicleCalc`/`VehicleModification` — отсылка на уникальность пары |

            ### Вне объёма

            `src/Nts.Domain` не существует — backend-реализация (EF-конфигурация, уникальный
            индекс) не часть этого CR, зафиксирована как требование к будущей реализации в тексте
            {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Domain.DOC_SelectedModifications>()}.
            """;

        public static string Plan = """
            ## План

            1. `SelectedModifications.cs` — правка спецификации (сделано).
            2. Собрать (`dotnet build`, `build-html.cmd`) (сделано).
            3. `doc` — проверка.
            4. Зафиксировать «Итог».
            """;

        public static string Acceptance = """
            ## Acceptance criteria

            - **AC-1.** Сущность `SelectedModifications` имеет поле `Id` (та же конвенция, что и
              у остальных доменных сущностей модуля).
            - **AC-2.** Документ явно фиксирует уникальность пары (`VehicleCalc`,
              `VehicleModification`) как отдельное требование — не выводится из наличия `Id`.
            - **AC-3.** Прозу «отдельного идентификатора... нет» убрано — не осталось
              противоречащих новому состоянию утверждений.
            """;

        public static string Result = """
            ## Итог

            Выполнено. `SelectedModifications` получила `Id` (та же конвенция `[DomainField(Alias
            = "id", RequiredOnCreate = true, Range = "UUID", Immutable = true)]`, что у всех
            остальных сущностей модуля), уникальность пары (`VehicleCalc`, `VehicleModification`)
            зафиксирована явно в `Purpose`.

            `doc` нашёл 1 находку: факт про `Id`/уникальность был изложен дважды (в XML-doc класса
            и в `Purpose`) почти идентичными, но не идентичными формулировками — введено этой же
            правкой. Исправлено: XML-doc класса сокращён до краткого назначения с отсылкой «см.
            Purpose», полное изложение осталось в одном месте. Остальные проверки (структурные
            связи, blast radius, согласованность атрибутов `Id` с остальными 21 сущностью модуля,
            факты CR про `BaseEntity`/`Ntr.Domain`) — без находок, прецедент в реальном коде
            подтверждён `doc` фактической проверкой (26/26 сущностей через `BaseEntity`).
            """;
    }
}