using System;
using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Icd;
namespace Ban.Sdaid.Icd.Arch.Tech.CSharp
{
/// <summary>
/// ADR-SRV-003: Правила создания доменных сущностей и их маппинга в EF Core. Обязательный
/// пошаговый контракт: домен по спеке → явная Fluent-конфигурация → DbSet, со ссылкой на спеку в коде.
/// </summary>
public class ADR_SRV_003_EntityAndEfMapping : IAdrDocument, IFolder<ArchCSharpFolder>
{
public string Name => "ADR-SRV-003. Сущности и EF-маппинг";
public string Description =>
@"На бэке формируются доменная модель и слой хранения на EF Core. Без единых правил создания
сущностей и их маппинга расходятся домен, конфигурация БД и DbContext. Фиксируем обязательный проверяемый
порядок: доменная сущность по спеке → явная Fluent-конфигурация → DbSet, с ссылкой на артефакт спеки в коде.";
public string Version => "1.0";
public string Status => "accepted";
public string[] Comments => new[]
{
"2026-08-12. Принято вместе с ADR-SRV-001/002. БД/EF ещё не введены — правило действует с появлением персистентности (первый write-срез).",
};
public Type? Supersedes => null;
public static string S1_Context = """
## Контекст и постановка задачи
Доменные сущности описаны в спеке (`50_Domain/<Module>`). При переносе в код (`Ban.Icd.Domain`)
и хранении на EF Core (`Ban.Icd.Infrastructure`) без единых правил быстро возникают расхождения
между доменной моделью, конфигурацией БД и `DbContext`: неявные связи, разнобой в именовании,
труднопроверяемые ревью.
Нужен обязательный, воспроизводимый и проверяемый порядок добавления сущности — чтобы реализация
была предсказуемой, а расхождение spec ↔ code ловилось на ревью, а не в рантайме. БД/EF ещё не
введены; правило вступает в силу с появлением персистентности.
""";
public static string S2_DecisionDrivers = """
## Драйверы решения
1. **Соответствие доменной спеке** — сущность повторяет модель и инварианты из `50_Domain`.
2. **Предсказуемость** — одинаковый порядок файлов и шагов для любой сущности.
3. **Меньше ошибок в EF-конфигурации** — связи и хранение заданы явно.
4. **Единообразие именования** связей и коллекций.
5. **Проверяемость на ревью** — изменение модели данных видно и обосновано.
""";
public static string S3_ConsideredOptions = """
## Рассмотренные варианты
- **A. Свободный порядок** — каждый добавляет сущность и маппинг как удобно. Быстро, но копятся
расхождения.
- **B (выбран). Обязательный пошаговый контракт** создания сущности.
- **C. Только рекомендации** — без обязательности; на практике не соблюдается.
""";
public static string S4_DecisionOutcome = """
## Решение
Выбран **Вариант B** — минимально достаточный проверяемый контракт добавления сущности:
1. **Доменная сущность** создаётся по доменной модели и её ограничениям из спеки `50_Domain`.
2. Для сущности заводится **явная EF-конфигурация** (Fluent API, `IEntityTypeConfiguration<T>`).
3. Сущность добавляется в `DbContext` через **`DbSet<T>`**, имя набора — во множественном числе.
4. Ссылки на другие сущности — как **`{Something}Id`**; навигационные связи и правила
(cascade и т. п.) задаются **в EF-конфигурации**, не в сущности.
5. При любой несостыковке код ↔ спека ↔ ожидаемое поведение — **задать вопрос пользователю**,
не выбирать трактовку молча.
6. В коде сущности — **явная ссылка на файл спеки**, по которому она создана
(XML-`<remarks>` с путём к артефакту `50_Domain/...`; правило ссылки — в `CLAUDE.md`).
**Разделение ответственности:** логика предметной области — в доменном слое; детали хранения
(типы колонок, длины, индексы, связи) — в EF-конфигурации. Изменение связи между сущностями —
архитектурно значимо и проверяется отдельно.
### Пример
```csharp
/// <remarks>Спека: sdaid/Ban.Sdaid.Icd/50_Domain/Capture/InputPacket.cs</remarks>
public sealed class InputPacket
{
public required string Number { get; set; }
public IReadOnlyList<ImageFrame> Frames { get; set; }
// FK — как {Something}Id; навигации и cascade — в конфигурации
}
public sealed class InputPacketConfiguration : IEntityTypeConfiguration<InputPacket>
{
public void Configure(EntityTypeBuilder<InputPacket> builder)
{
builder.Property(x => x.Number).HasMaxLength(32);
builder.HasMany(x => x.Frames).WithOne().HasForeignKey(x => x.PacketId); // cascade
}
}
// DbContext
public DbSet<InputPacket> InputPackets { get; set; }
```
""";
public static string S5_PositiveConsequences = """
## Положительные следствия
- Добавление сущности однотипно и быстро проверяется.
- Меньше неявных/ошибочных связей в модели данных.
- `DbContext` консистентен по составу наборов.
- Ссылка на спеку в коде связывает сущность с источником правды.
""";
public static string S6_NegativeConsequences = """
## Отрицательные следствия и компромиссы
- Больше явного кода — отдельная конфигурация на каждую сущность.
- На старте добавление простой сущности чуть дольше.
""";
public static string S7_Validation = """
## Проверка
- Новая сущность проходит чеклист 1–6 из этого ADR.
- В изменении присутствуют: доменная сущность, EF-конфигурация, `DbSet` в `DbContext`.
- Имена FK соответствуют шаблону `{Something}Id`.
- У сущности есть `<remarks>`-ссылка на артефакт спеки; при расхождении зафиксирован вопрос
пользователю до слияния.
""";
public static string S8_OpenQuestions = """
## Открытые вопросы / отложено
- Шаблон именования таблиц/индексов в EF-конфигурациях — фиксировать ли отдельно.
- Требовать ли seed-данные для каждой справочной сущности (сейчас справочники ввода отдаются
in-memory провайдером до появления БД).
- Разделение read/write-контекстов (read-only проекции) — вводится вместе с БД.
""";
public static string S99_Related = $"""
## Связанные артефакты
- {nameof(Ban.Sdaid.Icd.Arch.Tech.CSharp.ADR_SRV_001_Cqrs)} — CQRS: команды работают с сущностями через единицу работы.
- {nameof(Ban.Sdaid.Icd.Arch.Tech.CSharp.ADR_SRV_002_GridQueryContract)} — списки строятся на внутренних моделях/проекциях сущностей.
""";
}
}