Внедрить фабрики объектов вместо прямых new #12

Closed
opened 2026-09-13 00:35:45 +03:00 by stepan · 7 comments
Owner

Контекст

Владельцу не нравится множество объектов, создаваемых прямым new в коде. Паттерн: есть логика
(реализация фабрики) и интерфейс фабрики, который возвращает интерфейс готового объекта.
Если объект собирается итеративно (много частей/опций) — добавляется билдер.
DTO не касаемся: у них нет интерфейсов, они создаются по месту и не меняются.
Статичные хэлперы не трогаем (уточнение владельца): обычно хэлперы — статичные классы, фабрики для них не заводим.

Что сделать

  1. Инвентаризация: места создания нетривиальных объектов через new (сервисы/хелперы/компоненты, не DTO).
  2. Для каждой группы — фабрика: IXxxFactory + XxxFactory, метод возвращает интерфейс готового объекта.
  3. Для итеративно собираемых объектов — билдер (IXxxBuilder/XxxBuilder), фабрика делегирует билдеру.
  4. Перевести места создания на фабрики; прямые new реализаций убрать (остаются только внутри фабрик).

Критерии приёмки

  • вне фабрик нет new нетривиальных объектов (кроме DTO/исключений/примитивов)
  • сборка 5 решений 0/0, тесты зелёные
  • правила зафиксированы в код-стайле

Затрагивает

  • core (сервисы, инфраструктура), тесты; фронт не трогаем

Источники

  • Запрос владельца 2026-09-12
## Контекст Владельцу не нравится множество объектов, создаваемых прямым `new` в коде. Паттерн: есть логика (реализация фабрики) и **интерфейс фабрики**, который возвращает **интерфейс готового объекта**. Если объект собирается итеративно (много частей/опций) — добавляется **билдер**. **DTO не касаемся**: у них нет интерфейсов, они создаются по месту и не меняются. **Статичные хэлперы не трогаем** (уточнение владельца): обычно хэлперы — статичные классы, фабрики для них не заводим. ## Что сделать 1. Инвентаризация: места создания нетривиальных объектов через `new` (сервисы/хелперы/компоненты, не DTO). 2. Для каждой группы — фабрика: `IXxxFactory` + `XxxFactory`, метод возвращает интерфейс готового объекта. 3. Для итеративно собираемых объектов — билдер (`IXxxBuilder`/`XxxBuilder`), фабрика делегирует билдеру. 4. Перевести места создания на фабрики; прямые `new` реализаций убрать (остаются только внутри фабрик). ## Критерии приёмки - [x] вне фабрик нет `new` нетривиальных объектов (кроме DTO/исключений/примитивов) - [x] сборка 5 решений 0/0, тесты зелёные - [x] правила зафиксированы в код-стайле ## Затрагивает - core (сервисы, инфраструктура), тесты; фронт не трогаем ## Источники - Запрос владельца 2026-09-12
Owner

В описании неточности, обычно хэлперы это статичные классы, их мы не трогаем

В описании неточности, обычно хэлперы это статичные классы, их мы не трогаем
rust added this to the Deal Board project 2026-09-13 00:41:20 +03:00
rust moved this to To Do in Deal Board on 2026-09-13 00:41:29 +03:00
Author
Owner

Принято: статичные хэлперы (static class) не трогаем — фабрики только для нетривиальных объектов с интерфейсами. Описание задачи уточнено. Возьму в работу после закрытия задачи #1 (NSubstitute).

Принято: статичные хэлперы (static class) не трогаем — фабрики только для нетривиальных объектов с интерфейсами. Описание задачи уточнено. Возьму в работу после закрытия задачи #1 (NSubstitute).
rust moved this to In Progress in Deal Board on 2026-09-13 03:29:55 +03:00
stepan self-assigned this 2026-09-13 03:34:25 +03:00
Author
Owner

Беру в работу: инвентаризация new и фабрики для нетривиальных объектов с интерфейсами.

Беру в работу: инвентаризация `new` и фабрики для нетривиальных объектов с интерфейсами.
Author
Owner

Реализовано:

  • Фабрики: ISecretCipherFactory, ITenantLimitStoreFactory, IAiClassifierFactory, IAiToolsFactory, IFileStorageFactory (+ LocalStorageRoot).
  • Регистраторы (AddDealPersistence/AddDealIntegrations/AddDealSecurity, AddDealFileStorage) и DiscoveryWorkerService больше не создают реализации напрямую; счётчик ошибок поиска стал обязательной зависимостью.
  • Правило фабрик/билдеров и список исключений зафиксированы в код-стайле (§4.1).
  • Тест FileStorageFactory (Local/MinIO).

Проверки: сборка 5 решений 0/0; тесты core 1342, telegram 130, ai 52, ml 38, storage 9.

Область/исключения (прямой new без порт-интерфейса или не прикладной объект):

  • EF-конфигурации (IEntityTypeConfiguration, modelBuilder.ApplyConfiguration(new X())) — метаданные модели EF, а не прикладные объекты;
  • gRPC-соединения Ai/Ml/TelegramGrpcConnection — обёртки ресурсов с IDisposable без порт-интерфейса;
  • статичные хэлперы и объекты без порт-интерфейса, создаваемые контейнером (AddScoped<Concrete>()).

Открытый вопрос: считать ли EF-конфигурации объектами под это правило? Если да — переведу оба контекста на ApplyConfigurationsFromAssembly (потребует разделения namespace конфигураций).

Билдеры в текущем объёме не потребовались: все целевые объекты собираются одним конструктором с DI-зависимостями (не итеративно). Правило для билдеров зафиксировано в код-стайле на будущее.

Реализовано: - Фабрики: `ISecretCipherFactory`, `ITenantLimitStoreFactory`, `IAiClassifierFactory`, `IAiToolsFactory`, `IFileStorageFactory` (+ `LocalStorageRoot`). - Регистраторы (`AddDealPersistence`/`AddDealIntegrations`/`AddDealSecurity`, `AddDealFileStorage`) и `DiscoveryWorkerService` больше не создают реализации напрямую; счётчик ошибок поиска стал обязательной зависимостью. - Правило фабрик/билдеров и список исключений зафиксированы в код-стайле (§4.1). - Тест `FileStorageFactory` (Local/MinIO). Проверки: сборка 5 решений 0/0; тесты core 1342, telegram 130, ai 52, ml 38, storage 9. Область/исключения (прямой `new` без порт-интерфейса или не прикладной объект): - EF-конфигурации (`IEntityTypeConfiguration`, `modelBuilder.ApplyConfiguration(new X())`) — метаданные модели EF, а не прикладные объекты; - gRPC-соединения `Ai/Ml/TelegramGrpcConnection` — обёртки ресурсов с `IDisposable` без порт-интерфейса; - статичные хэлперы и объекты без порт-интерфейса, создаваемые контейнером (`AddScoped<Concrete>()`). Открытый вопрос: считать ли EF-конфигурации объектами под это правило? Если да — переведу оба контекста на `ApplyConfigurationsFromAssembly` (потребует разделения namespace конфигураций). Билдеры в текущем объёме не потребовались: все целевые объекты собираются одним конструктором с DI-зависимостями (не итеративно). Правило для билдеров зафиксировано в код-стайле на будущее.
Author
Owner

MR: #14

MR: https://gitea.khomegeneric.keenetic.pro/rust/Deal/pulls/14
rust moved this to Review in Deal Board on 2026-09-13 03:49:08 +03:00
Author
Owner

Стабильность тестов исправлена (коммит в MR #14).

Причина флейков: TelegramIngressTestHost мутировал процессную переменную DEAL_SERVICE_TOKEN, а три ingress-тест-класса (IngressRateLimitInterceptorTests, TelegramIngressServiceTests, SourceIngressGrpcServiceTests) идут параллельно и перетирали значение друг другу (сервер читал чужой токен → не тот код ответа).

Фикс: токен задаётся in-memory конфигурацией хоста (builder.Configuration.AddInMemoryCollection), глобальный env больше не трогается. IngressServiceTokenInterceptor читает токен из конфигурации, поэтому поведение прод-кода не менялось.

Проверка: 18 прогонов полного набора подряд — все зелёные (ранее падало ~1 из 3).

Стабильность тестов исправлена (коммит в MR #14). Причина флейков: `TelegramIngressTestHost` мутировал процессную переменную `DEAL_SERVICE_TOKEN`, а три ingress-тест-класса (`IngressRateLimitInterceptorTests`, `TelegramIngressServiceTests`, `SourceIngressGrpcServiceTests`) идут параллельно и перетирали значение друг другу (сервер читал чужой токен → не тот код ответа). Фикс: токен задаётся in-memory конфигурацией хоста (`builder.Configuration.AddInMemoryCollection`), глобальный env больше не трогается. `IngressServiceTokenInterceptor` читает токен из конфигурации, поэтому поведение прод-кода не менялось. Проверка: 18 прогонов полного набора подряд — все зелёные (ранее падало ~1 из 3).
Author
Owner

Исправлено замечание владельца по код-стайлу: метод Create фабрик теперь реализован явно (ISecretCipher ISecretCipherFactory.Create()), интерфейс вызывается только по порту, дублей summary нет. Аудит правила по всему проекту — задача #15.

Исправлено замечание владельца по код-стайлу: метод Create фабрик теперь реализован явно (ISecretCipher ISecretCipherFactory.Create()), интерфейс вызывается только по порту, дублей summary нет. Аудит правила по всему проекту — задача #15.
rust closed this issue 2026-09-13 04:25:52 +03:00
rust moved this to Done in Deal Board on 2026-09-13 04:26:00 +03:00
Sign in to join this conversation.