Files
mapo-core/MP.IOC/refactor_plan.md
T

13 KiB

Piano di Refactoring: MP.IOC

Obiettivo

Modernizzare il progetto MP.IOC allineandolo agli standard architettonici definiti in MP.SPEC:

  1. Rimuovere l'uso di MpIocController come servizio di accesso dati diretto
  2. Migrare la logica di MpDataService ai repository esistenti (IIocRepository, IStatsAggrRepository, etc.)
  3. Standardizzare il caching manuale Redis → FusionCache GetOrFetchAsync
  4. Correggere tutti gli anti-pattern statici e DI

Stato Attuale

File Riga Tipo Descrizione
IOBController.cs 1675 Controller ASP.NET 52 endpoints REST (api/IOB)
BenchController.cs 387 Controller ASP.NET Test/bench Redis
RecipeController.cs 73 Controller ASP.NET API ricette (2 metodi)
RecipeArchiveController.cs 130 Controller ASP.NET API ricette file (2 metodi)
MpDataService.cs 3475 Data Hub Singleton, ~100 metodi, bypassa repository
MpIocController.cs 1480 ⚠️ Wrong location 82 metodi EFCore in MP.Data, usato come servizio

Struttura DI Attuale

Program.cs
├── AddIocDataLayer() da MP.Data
│   ├── MpIocController ── Singleton ← usato direttamente da MpDataService
│   ├── IIocRepository ── Scoped ← mai usato da MpDataService
│   ├── IStatsAggrRepository ── Scoped ← mai usato
│   └── IStatsDetailRepository ── Scoped ← mai usato
├── MpDataService ── Singleton ← chiama IocDbController (statico)
└── IFusionCache ── L1 Memory only, NO Redis backplane

Problemi Chiave

  1. MpIocController come servizio — 82 metodi EFCore classificati ingannevolmente come "Controller" ma in realtà sono un servizio di accesso dati, registrato come Singleton con DbContext che non è thread-safe
  2. MpDataService bypassa repository — ~80% dei metodi chiama IocDbController.XXX() direttamente invece di usare IIocRepository
  3. Nessun FusionCache — MpDataService usa manual redisDb.StringGetAsync/StringSetAsync (~80% del codice) invece di IFusionCache.GetOrFetchAsync
  4. Singleton lifetime mismatch — MpDataService è Singleton, registra campi statici per IoC Controller e Mongo Controller che vengono sovrascritti

Piano di Refactoring

Fase 1: Pulizia Anti-Pattern (Quick Wins)

Obiettivo: risolvere gli Static fields e i problemi DI più evidenti senza cambiare architettura.

1.1 Rimuovere static IConfiguration dai 4 controller

File Fix
IOBController.cs:23 readonly IConfiguration _configuration (usato solo nel ctor, rimosso se dead)
BenchController.cs:19 Rimuovere campo statico (non usato dopo ctor)
RecipeController.cs:18 Rimuovere campo statico (non usato dopo ctor)
RecipeArchiveController.cs:18 Rimuovere campo statico (non usato dopo ctor)

File da modificare:

  • C:\Users\samuele.steamw\source\MAPO-CORE\MP.IOC\Controllers\IOBController.cs
  • C:\Users\samuele.steamw\source\MAPO-CORE\MP.IOC\Controllers\BenchController.cs
  • C:\Users\samuele.steamw\source\MAPO-CORE\MP.IOC\Controllers\RecipeController.cs
  • C:\Users\samuele.steamw\source\MAPO-CORE\MP.IOC\Controllers\RecipeArchiveController.cs

1.2 Correggere MpIocController singleton

MpIocController usa IDbContextFactory correttamente, ma è registrato come Singleton in AddIocDataLayer(). I DbContext Factory sono thread-safe ma il pattern Singleton è inutile e potenzialmente pericoloso.

Fix: registrare come Scoped in DataServiceCollectionExtensions.cs:

services.TryAddScoped<IMpIocService, MpIocController>();  // rinominare in servizio
// oppure creare interfaccia IMpIocRepository

1.3 Rimuovere campi statici in MpDataService

Campo Righe Fix
public static MpMongoController mongoController 94 Rimuovere campo statico → iniettare via contructor o renderlo readonly
public static MpIocController IocDbController 95 Bloccante — usato da ~45+ metodi. Richiede Fase 3 completa

Fase 2: Abilitare FusionCache per MP.IOC

Attualmente MP.IOC ha FusionCache disabilitato (L1 solo, Redis non connesso).

2.1 Configurazione Redis + FusionCache in Program.cs

// Attuale (solo L1 Memory)
builder.Services.AddFusionCache()
    .WithDefaultEntryOptions(options => { options.Duration = TimeSpan.FromMinutes(1); });

// Nuovo (L1 Memory + L2 Redis + L3 DB)
var redisMux = ConnectionMultiplexer.Connect(confRedis);
builder.Services.AddSingleton<IConnectionMultiplexer>(redisMux);

builder.Services.AddFusionCache()
    .WithDistributedCache(sp => sp.GetRequiredService<IDistributedCache>())
    .WithSerializer(new FusionCacheNewtonsoftJsonSerializer())
    .WithBackplane(new RedisBackplane(new RedisBackplaneOptions {
        ConnectionMultiplexerFactory = () => Task.FromResult(redisMux)
    }));

2.2 Migrazione metodi MpDataService a GetOrFetchAsync

Metodi da migrare (identificazione preliminare):

Categoria Metodi Stima righe
Anagrafica AnagStatiGetAllAsync, ArticoliGetLastByMaccAsync, ConfigGetAllAsync, DecNumArtGetFiltAsync ~120
Produzione OdlCurrByMaccAsync, OdlLastByMaccAsync, POdlGetByMaccArtAsync, PezziProdMacchinaAsync, StatoProdMacchinaAsync ~200
FluxLog ConfFluxFiltAsync, FluxLogFirstByMaccAsync, FluxLogGetLastFiltAsync, FluxLogTakeSnapshotLastAsync ~100
Dossier DossGetLastByMaccAsync ~50
Machine DatiMacchineGetAllAsync, MseGetAllAsync, Macchine2SlaveAsync, MacchineGetByIdxAsync ~150
MicroStato EvListMicroStatoInsertAsync, MicroStatoMacchinaUpsertAsync, MicroStatoMacchinaGetByIdxMaccAsync ~80
VMSFD VMSFDGetByMaccAsync, VMSFDGetMultiByMaccAsync, StateMachIngressiAsync ~80
Misc Alarms, RegScarti, RegControlli, RemRebootLog, SignalLog, KeepAlive ~150

Totale stimato: ~930 righe di codice caching da migrare a pattern GetOrFetchAsync.

Fase 3: Decomposizione MpIocController in Repository (Core)

MpIocController (1480 righe, 82 metodi) va scomposto in repository specialistici, come fatto per MP.SPEC:

3.1 Mappatura attuali → Nuovi Repository

Attuale (82 metodi) Nuovo Repository Ambito
AnagStatiGetAllAsync, ArticoliGetLastByMaccAsync, ConfigGetAllAsync, ConfigUpdateAsync, DecNumArtGetFiltAsync, ListValuesFiltAsync, ListLinkFiltAsync, EvListInsert IAnagRepository Usato da SPEC — già migrato!
AutoStartOdlAsync, ConfirmaProdMacchinaAsync/Full, OdlCurrByMaccAsync, OdlLastByMaccAsync, OdlListByMaccPeriodoAsync, PezziProdMacchinaAsync, StatoProdMacchinaAsync IProduzioneRepository (nuovo) Produzione IOB-specifica
OdlAutoDayGenAsync/Full, POdlGetByMaccArtAsync, OdlFixMachineSlave IProduzioneRepository (nuovo) Pianificazione produzione
FluxLogInsertAsync, FluxLogGetLastFiltAsync, FluxLogFirstByMaccAsync, FluxLogTakeSnapshotLastAsync, ConfFluxFiltAsync IFluxLogRepository Usato da SPEC — già migrato!
DossGetLastByMaccAsync, SignalLogInsertAsync IDossierRepository Usato da SPEC — già migrato!
DatiMacchineGetAllAsync, MacchineGetByIdxAsync, MacchineUpsertAsync, MacchineGetFiltAsync, MacchineGetAllAsync, Macchine2Slave IMacchineRepository (nuovo) Gestione macchine
EvListMicroStatoInsertAsync, MicroStatoMacchinaUpsertAsync, MicroStatoMacchinaGetByIdxMaccAsync, MicroStatoMacchinaGetByIdxMaccAsync IMicroStatoRepository (nuovo) Micro-stati macchine
MSE_getUserForcedAsync, SMES_getHwTransitionsAsync, DDB_InsStatoBatchAsync, CheckCambiaStatoBatchAsync, RecalcMseAsync IMseRepository (nuovo) Mappa stati macchin
StateMachineIngressiAsync, VMSFDGetByMaccAsync, VMSFDGetMultiByMaccAsync, VMSFDGetAllAsync IStateMachineRepository (nuovo) State machine
KeepAliveUpsertAsync IMacchineRepository (nuovo) Keep-alive
AlarmLogInsertAsync, RegScartiInsertAsync, RegControlliInsertAsync, RegDichiarInsertAsync IRegistroRepository (nuovo) Registri qualità
RemRebootLog* methods (6) IRemRebootRepository (nuovo) Reboot remote logging

3.2 Interfacce Nuove (da creare in MP.Data/Repository/)

MP.Data/Repository/Iob/
├── IProduzioneRepository.cs
├── ProduzioneRepository.cs
├── IMacchineRepository.cs
├── MacchineRepository.cs
├── IMicroStatoRepository.cs
├── MicroStatoRepository.cs
├── IMseRepository.cs
├── MseRepository.cs
├── IStateMachineRepository.cs
├── StateMachineRepository.cs
├── IRegistroRepository.cs
├── RegistroRepository.cs
└── IRemRebootRepository.cs
    └── RemRebootRepository.cs

3.3 Migrazione Dipendenze

Dopo aver creato i repository, ogni metodo in MpDataService.cs che chiama IocDbController.XXX() va aggiornato per chiamare il repository invece.

Esempio:

// PRIMA
var result = await IocDbController.AnagStatiGetAllAsync();

// DOPO
var result = await _anagRepository.AnagStatiGetAllAsync();

3.4 Rimozione MpIocController

Dopo la migrazione completa:

  1. Rimuovere MpIocController da DataServiceCollectionExtensions.cs
  2. Rimuovere il parametro dal costruttore di MpDataService
  3. Spostare i metodi ancora non migrati a #if false come fallback documentato

Fase 4: Scomposizione MpDataService

MpDataService.cs (3475 righe) è troppo grande per essere un unico servizio. Proposta di scomposizione:

Servizio Responsabilità Metodi
MpDataService (core) Gateway, orchestrazione ~50
IodlManagementService ODL lifecycle (apri, chiudi, split, conferma) ~15
FluxLogProcessingService Processing flux log, snapshot, pareto ~10
MachineCommunicationService Keep-alive, dati macchin, micro-stati, state machine ~20
ProductionTrackingService Conferma produzione, pezzi prodotti, stato produzione ~8
RedisCacheService Wrapper methods per Redis operations (Hash, String, Set) ~15

Nota: La scomposizione va fatta dopo la migrazione a repository (Fase 3) per non dover modificare ogni singola chiamata.

Fase 5: Build & Verifica

Dopo ogni fase completa:

  1. dotnet build MP-IOC.sln
  2. dotnet build MP-SPEC.sln (non deve rompersi)
  3. dotnet build MP-LAND.sln
  4. dotnet build MP-MON.sln
  5. Verificare che MP.IOC parta correttamente

Riepilogo Costi Stimati

Fase File Modificati Righe Modificate Rischio Stato
F1: Anti-Pattern 4 controller 4 20 Basso Completato
F2: FusionCache config + metodi 2 + 1 ~1200 Medio Da fare
F3: Repository IoC 7+ nuovi + 5 esistenti ~2000 Alto Da fare
F4: Scomposizione MpDataService 5 nuovi + 1 esistente ~3475 Alto Da fare
F5: Build & Verifica Basso OK (0 errori, 12 warnings)

Stato Completato

Fase 1: Anti-Pattern Controller — COMPLETATA

Rimosso static IConfiguration _configuration da tutti i 4 controller e rimossa la dead assignment dai costruttori:

Controller Prima Dopo
IOBController IConfiguration + static field Nessun param (dead code), field rimosso
BenchController IConfiguration + static field Solo MpDataService, field rimosso
RecipeController IConfiguration + static field Solo MpDataService, field rimosso
RecipeArchiveController IConfiguration + static field Solo MpDataService, field rimosso

Build risultato: 0 errori, 12 warnings (solo nullable reference, nessun warning statico residuo nei controller)

📋 Fase 2+: In Attesa

Prossimo step: abilitare FusionCache con Redis backplane (Fase 2) — richiede approvazione architetturale.

Priorità Operativa

  1. F1 immediata — zero rischio, fix 4 controller statici
  2. F2 parallela — abilitare FusionCache con Redis backplane (config) + migrare i metodi più usati
  3. F3 dopo F2 — decomporre MpIocController una volta che il caching è standardizzato
  4. F4 opzionale — scomporare MpDataService quando il resto è stabile

Note

  • MpIocController usa già IDbContextFactory correttamente — il problema è che è un Singleton e lo si chiama da un altro Singleton (MpDataService)
  • IIocRepository (già esistente) ha solo 16 metodi ed è mai usato da MpDataService
  • I repository SPEC (Anag, Dossier, FluxLog, Production) sono già usati da alcuni metodi — sfruttare questi dove possibile
  • La struttura MP.Data/Controllers/ è ingannevole: MpIocController è un servizio, non un controller Web API