13 KiB
Piano di Refactoring: MP.IOC
Obiettivo
Modernizzare il progetto MP.IOC allineandolo agli standard architettonici definiti in MP.SPEC:
- Rimuovere l'uso di
MpIocControllercome servizio di accesso dati diretto - Migrare la logica di
MpDataServiceai repository esistenti (IIocRepository,IStatsAggrRepository, etc.) - Standardizzare il caching manuale Redis → FusionCache
GetOrFetchAsync - 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
- 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
- MpDataService bypassa repository — ~80% dei metodi chiama
IocDbController.XXX()direttamente invece di usareIIocRepository - Nessun FusionCache — MpDataService usa manual
redisDb.StringGetAsync/StringSetAsync(~80% del codice) invece diIFusionCache.GetOrFetchAsync - 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.csC:\Users\samuele.steamw\source\MAPO-CORE\MP.IOC\Controllers\BenchController.csC:\Users\samuele.steamw\source\MAPO-CORE\MP.IOC\Controllers\RecipeController.csC:\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:
- Rimuovere
MpIocControllerdaDataServiceCollectionExtensions.cs - Rimuovere il parametro dal costruttore di
MpDataService - Spostare i metodi ancora non migrati a
#if falsecome 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:
dotnet build MP-IOC.slndotnet build MP-SPEC.sln(non deve rompersi)dotnet build MP-LAND.slndotnet build MP-MON.sln- Verificare che MP.IOC parta correttamente
Riepilogo Costi Stimati
| Fase | File Modificati | Righe Modificate | Rischio | Stato |
|---|---|---|---|---|
| ✅ 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
- F1 immediata — zero rischio, fix 4 controller statici
- F2 parallela — abilitare FusionCache con Redis backplane (config) + migrare i metodi più usati
- F3 dopo F2 — decomporre MpIocController una volta che il caching è standardizzato
- F4 opzionale — scomporare MpDataService quando il resto è stabile
Note
- MpIocController usa già
IDbContextFactorycorrettamente — 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