
Parliamoci chiaro fin da subito.
Magari hai sentito questo termine, UI composition, buttato lì da qualche tecnico durante una riunione o letto in qualche blog pieno di paroloni.
Ti avranno fatto credere che sia l'ultima frontiera dello sviluppo software, una specie di formula magica per creare interfacce grafiche desktop (pensa alle tue app per Windows) in modo super moderno e flessibile.
L'idea in sé non è complicata: invece di costruire la tua finestra "tutta d'un pezzo", la si assembla mettendo insieme tanti piccoli mattoncini indipendenti, chiamati "componenti" o "moduli".
L'idea è che questi mattoncini siano riutilizzabili, testabili singolarmente, facili da aggiornare. Un po' come i Lego, no?
Esistono anche strumenti software, librerie specifiche per sviluppatori (qualcuno potrebbe aver menzionato nomi altisonanti), che sono nate proprio per facilitare questo approccio.
Promettono meraviglie: software flessibile, scalabile, a prova di futuro... tutte belle parole che riempiono le slide.
Ma la domanda vera è: serve davvero a te? Serve al tuo progetto specifico, con le tue risorse, il tuo team, il tuo budget? Serve a risolvere un problema che hai realmente?
Nella mia esperienza, accumulata in tanti anni passati a vedere progetti software nascere, crescere e purtroppo a volte schiantarsi proprio per colpa di scelte tecniche inadeguate, la risposta è quasi sempre un secco NO.
Parlo della realtà che conosco meglio: quella delle software house italiane e soprattutto di aziende produttive che sviluppano software internamente, dei team di sviluppo interni alle aziende, gruppi piccoli o medi (da 1 a 5 persone, a volte poco più) che lavorano sodo per creare gestionali, applicazioni per l'ufficio, utility specifiche.
In questo contesto, nel 99% dei casi, la UI composition come viene spesso proposta è totalmente inutile.
Non solo inutile, ma attivamente dannosa.
Perché? Perché introduce strati su strati di complessità tecnica dove non ce n'era alcun bisogno.
Complica il codice, allunga i tempi necessari per sviluppare qualsiasi funzionalità, e quindi gonfia i costi in modo ingiustificato.
Il tutto, senza portare un vantaggio tangibile né a te, che paghi, né all'utente finale, che usa l'applicazione.
È un esempio da manuale di sovraingegnerizzazione: usare un'arma nucleare per schiacciare una noce.
Si fa perché "è moderno", perché permette a qualche tecnico di sentirsi all'avanguardia, di aggiungere una sigla al curriculum, o magari perché un consulente esterno deve giustificare la sua (costosa) presenza proponendo soluzioni iper-sofisticate
Ma quasi mai perché risolve un problema reale del progetto in modo efficiente ed economico.
Nel nostro settore c'è una spinta continua verso la novità, verso il framework "del momento", verso il pattern architetturale che va di moda su Twitter.
Ma la vera abilità non sta nell'adottare acriticamente l'ultima tendenza.
Sta nel capire profondamente il problema che devi risolvere e scegliere lo strumento più semplice ed efficace per farlo, tenendo conto dei vincoli concreti: il tempo a disposizione, le persone che ci lavoreranno, e soprattutto il budget.
Ed è fondamentale che tu, come committente, manager o responsabile del progetto, sia armato di questo spirito critico per non farti incantare da promesse tecnologiche che potrebbero rivelarsi un boomerang costoso.
La promessa (fasulla): componenti riutilizzabili, modularità, mantenibilità

Chi ti propone la UI composition come la via maestra ti dipingerà un quadro idilliaco, basato su tre grandi promesse che, prese singolarmente, sembrano tutte ragionevoli.
Il problema è che, nel contesto delle applicazioni desktop standard sviluppate da piccoli team, queste promesse raramente si concretizzano come sperato, e spesso nascondono effetti collaterali indesiderati.
Vediamole una per una con occhio critico.
La sirena del riuso: componenti miracolosi?
La prima promessa è quella dei componenti riutilizzabili.
"Pensa che bello," ti diranno, "creiamo una volta sola la griglia per mostrare i dati, con tutte le funzioni di ordinamento, filtro e paginazione, e poi la usiamo in tutte le finestre: clienti, fornitori, articoli, ordini... un risparmio enorme!".
In teoria, magnifico.
Nella pratica, per far sì che quella griglia funzioni esattamente come serve in contesti così diversi, devi riempirla di opzioni, parametri, eventi personalizzati, logiche condizionali.
Deve adattarsi a tipi di dati diversi, a numeri di colonne variabili, a comportamenti specifici richiesti in una finestra ma non in un'altra.
Il risultato?
Quel componente "riutilizzabile" diventa un pezzo di software mostruosamente complesso, difficile da sviluppare, da testare in tutte le sue varianti e, soprattutto, da mantenere.
Spesso, finisci per spendere più tempo a creare e debuggare il componente "generico" di quanto avresti impiegato a creare 3 o 4 griglie specifiche, semplici e chiare, nei punti dove servivano.
Il riuso spinto all'estremo, specialmente nell'interfaccia utente che ha sempre tante piccole eccezioni, è spesso una trappola di complessità.
Il mito della modularità indipendente
La seconda promessa è quella della modularità.
"Dividiamo l'applicazione in moduli!", ti suggeriranno.
Un modulo per le anagrafiche, uno per la fatturazione, uno per il magazzino. Così sono indipendenti, ognuno può essere sviluppato e testato per conto suo, magari da persone diverse!
Di nuovo, suona bene, specialmente se pensi a progetti enormi.
Ma caliamolo nella tua realtà: un team di 3-5 persone che lavora sullo stesso gestionale.
Dividere fisicamente il codice in moduli separati (spesso addirittura progetti diversi dentro Visual Studio) introduce un sacco di complicazioni pratiche.
Devi gestire le dipendenze tra questi moduli (perché, ammettiamolo, non saranno mai veramente indipendenti: la fatturazione avrà bisogno dei dati delle anagrafiche!).
Devi configurare meccanismi complessi per farli comunicare tra loro.
Devi gestire il processo di compilazione e installazione di tutti questi pezzi separati.
Per un piccolo team, questa "modularità" fisica è quasi sempre un appesantimento inutile.
Una buona organizzazione logica all'interno di un unico progetto è molto più efficiente.
Non confondere la separazione fisica dei file con la separazione logica delle responsabilità.
L'illusione della manutenzione semplificata
La terza promessa è la conseguenza delle prime due.
Se tutto è fatto a pezzi piccoli e indipendenti, sarà più facile fare manutenzione in futuro!
Se devi cambiare la logica di calcolo dell'IVA, modifichi solo il modulo fatturazione!
Vero, la modifica è localizzata.
Ma l'impatto della modifica?
Devi essere sicurissimo che cambiando quel pezzetto non si rompa nulla in tutti gli altri moduli che magari lo usavano o che ricevevano dati da esso.
Devi testare tutte le interazioni.
E soprattutto, devi mantenere non solo il codice "funzionale", ma anche tutta l' "infrastruttura" che hai costruito per far funzionare questa architettura complessa: i sistemi per gestire le dipendenze, i bus per i messaggi, le interfacce astratte...
Spesso, la manutenzione si sposta dal modificare la logica di business (che magari era semplice) al debuggare i meccanismi astrusi dell'architettura stessa.
Queste tre promesse, pur valide in certi contesti estremi, per la maggior parte delle applicazioni desktop diventano un miraggio che ti porta fuori strada, facendoti accumulare costi aggiuntivi e complessità tecnica senza un ritorno proporzionato in termini di qualità o efficienza.
La realtà nei progetti veri (quelli italiani): cosa succede quando ci caschi

Mettiamo da parte la teoria e le slide patinate.
Voglio raccontarti cosa vedo succedere, quasi come un copione che si ripete, quando un team di sviluppo "normale", magari sotto la spinta di buone intenzioni ma poca esperienza pratica su larga scala, decide di abbracciare la filosofia della UI composition spinta per un progetto desktop standard.
Immagina la scena: devono sviluppare un software gestionale per un'azienda manifatturiera.
Funzionalità classiche: clienti, fornitori, magazzino, ordini, produzione, fatturazione.
Un progetto di medie dimensioni, ma fondamentale per il cliente.
Si decide di usare un'architettura "moderna" basata su composition e moduli separati.
I primi mesi sono dedicati all'"architettura" (con la A maiuscola).
Il team non parte creando la finestra dei clienti.
No, passa settimane a:
- Installare e configurare librerie software specifiche per la composition (il "framework").
- Decidere quanti moduli creare (Clienti? Clienti e fornitori insieme? Spedizioni è un modulo a sé?). Ore di discussioni accademiche.
- Impostare il sistema per la gestione delle dipendenze (un modo complesso per gestire come i pezzi si "parlano").
- Definire le "zone" nell'interfaccia principale (le aree dove verranno caricati dinamicamente i moduli).
- Stabilire il sistema di messaggistica per far comunicare i moduli tra loro.
Il tutto produce tanto codice "infrastrutturale", difficile da capire per chi non l'ha scritto, ma nessuna funzionalità visibile al cliente. Il tempo passa, il budget inizia a ridursi, e l'applicazione è ancora un fantasma. Questo è l'enorme costo nascosto dell'impostazione iniziale, un "investimento" in complessità che raramente ripaga su questa scala.
Poi si inizia a sviluppare, ma è una corsa a ostacoli.
Finalmente si creano le prime finestre.
Ma ogni operazione è più lenta e macchinosa del normale.
Per mostrare una lista di clienti, non basta caricarla e visualizzarla.
Bisogna creare un "componente" per la lista, un "componente" per la barra dei filtri, un "componente" per i pulsanti, ognuno col suo pezzo di logica separato.
Poi bisogna farli comunicare: il filtro deve dire alla lista di aggiornarsi, la lista deve dire ai pulsanti se un cliente è selezionato...
Il codice si riempie di eventi, messaggi, interfacce.
Tu o i tuoi sviluppatori, invece di concentrarsi sulla logica di business (es. come calcolare lo sconto cliente), passano metà del tempo a far funzionare l'architettura stessa, a "passare la palla" tra i vari componenti in modo corretto.
La produttività crolla.
Il momento del debug diventa un incubo ricorrente
Inevitabilmente, qualcosa non funziona. Un calcolo è sbagliato, un dato non si aggiorna, l'applicazione rallenta.
Trovare la causa è come cercare un ago in un pagliaio distribuito su dieci stanze diverse.
Il problema è nel componente A?
O nel componente B che gli manda i dati?
O nel servizio C che li ha caricati?
O nel messaggio D che non è partito?
Devi usare strumenti di debug avanzati, mettere breakpoint ovunque, ricostruire mentalmente il flusso intricato attraverso tutti i livelli di astrazione.
Ore preziose (e costose) vengono bruciate per risolvere problemi che in un sistema più semplice richiederebbero minuti.
Alla fine, ti accorgi della "falsa modularità"
Dopo mesi di lavoro, ti rendi conto che i moduli "indipendenti" non lo sono affatto.
Il modulo fatturazione è inutilizzabile senza quello clienti e quello ordini.
Cambiare una piccola cosa nel modulo magazzino richiede di aggiornare anche altri tre moduli.
La separazione fisica in progetti diversi è stata solo una complicazione: i moduli sono fortemente accoppiati logicamente.
Hai ottenuto solo gli svantaggi della distribuzione (complessità di build, deployment, comunicazione) senza i vantaggi dell'indipendenza.
Il risultato finale?
Un'applicazione consegnata in ritardo, costata molto più del previsto, più difficile da usare per gli sviluppatori stessi (e quindi da mantenere) e spesso meno stabile.
Una vittoria della tecnologia fine a se stessa, una sconfitta per il pragmatismo e per il tuo budget o quello del tuo cliente.
Perché spesso complichi l’interfaccia senza semplificare davvero il codice
Voglio approfondire un punto che secondo me è centrale: l'illusione che complicare la struttura tecnica del codice porti automaticamente a un beneficio, magari rendendo più "semplice" gestire interfacce complesse.
È un'idea sbagliata, basata su un fraintendimento di cosa sia la vera semplicità nello sviluppo software.
Pensa a come è fatta un'applicazione desktop che usi.
Ha delle finestre, giusto? Dentro le finestre ci sono pulsanti, campi di testo, tabelle, grafici.
L'utente interagisce con questi elementi.
La "complessità" per l'utente sta forse nella quantità di funzioni o nella chiarezza con cui sono presentate, non certo in come lo sviluppatore ha deciso di organizzare il codice "dietro le quinte".
Quando tu adotti una UI composition molto spinta, stai di fatto imponendo una struttura tecnica complessa (tanti piccoli pezzi, comunicazione indiretta, astrazioni) per rappresentare qualcosa che, dal punto di vista logico e visivo, potrebbe essere molto più semplice.
Stai aggiungendo strati intermedi tra l'intenzione ("voglio mostrare i dati del cliente") e il risultato ("vedo i dati del cliente sullo schermo").
Ogni strato di astrazione in più, ogni "componente" separato, ogni meccanismo di comunicazione indiretta, introduce diversi problemi concreti.
Aumenta la quantità di codice da scrivere, non solo quello utile ma anche quello "collante".
Rende più difficile leggere e capire il codice, perché devi seguire il flusso attraverso più file e livelli.
Moltiplica i punti in cui possono nascere bug, perché ogni interfaccia tra componenti è una potenziale fonte di errore.
Rende il debugging un'operazione più lunga e complessa.
Infine, introduce rigidità: cambiare qualcosa può richiedere modifiche a cascata su più livelli.
Tutto questo per ottenere, alla fine, la stessa finestra con gli stessi pulsanti e le stesse tabelle che avresti ottenuto con un approccio più diretto?
Dov'è il vantaggio?
Non c'è, se non in casi molto particolari (che vedremo).
Nella maggior parte dei progetti, questa complessità tecnica è solo zavorra.
Rallenta lo sviluppo, aumenta i costi, rende il software più fragile.
La vera eleganza nello sviluppo non sta nel creare architetture cervellotiche, ma nel trovare la soluzione più semplice possibile che risolva il problema in modo efficace e robusto.
La UI composition spinta, nel 99% dei casi, va nella direzione opposta.
Stai pagando un prezzo alto per una complessità che non ti serve.
Modularizzare troppo spesso significa solo nascondere la confusione

La modularità, l'idea di dividere un problema complesso in parti più piccole e gestibili, è una buona idea in generale.
Ma il diavolo, come si suol dire, sta nei dettagli.
E nel modo in cui viene applicata.
L'approccio modulare spinto dalla UI composition spesso porta a una frammentazione eccessiva e controproducente. Crei decine di moduli, magari ognuno nel suo progetto separato, pensando di aver raggiunto l'ordine perfetto.
Ma poi ti accorgi che questi moduli non sono affatto indipendenti.
Scopri che il modulo A ha bisogno di un dato che sta nel modulo B, il quale a sua volta deve scatenare un'azione nel modulo C.
Come fanno a parlarsi?
Devi introdurre meccanismi di comunicazione complessi: interfacce condivise, servizi che fanno da ponte, sistemi di messaggistica centralizzati...
Tutta infrastruttura aggiuntiva che devi progettare, implementare e mantenere.
Alla fine, l'indipendenza è solo un'illusione.
Hai sostituito le dipendenze dirette (e facili da vedere) all'interno di un unico progetto con dipendenze indirette (e difficili da tracciare) tra moduli separati.
È come cercare di mettere ordine in una scrivania caotica spostando ogni oggetto in una scatola diversa: hai tante scatole, ma la scrivania è ancora inutilizzabile perché per fare qualsiasi cosa devi aprire dieci scatole diverse.
Questa finta modularità è pericolosa.
Aumenta la complessità del processo di compilazione del software.
Complica l'installazione, perché devi assicurarti di distribuire tutti i pezzi giusti.
Rende più rischioso fare modifiche strutturali al codice (refactoring), perché non è immediatamente ovvio quali altri moduli potrebbero essere impattati.
E soprattutto, maschera i veri problemi di progettazione: invece di semplificare una logica troppo ingarbugliata, la spezzetti e la nascondi in moduli diversi, rendendola ancora più difficile da sistemare in seguito.
Per la stragrande maggioranza delle applicazioni desktop, una buona organizzazione logica all'interno di un singolo progetto è la chiave per la vera manutenibilità e chiarezza.
Non farti ingannare dalla sirena della modularità fisica a tutti i costi.
Il costo della sovraingegnerizzazione: tempi, bug e fatica (pagati da te)

Voglio essere brutalmente onesto su questo punto, parlando direttamente a te che magari gestisci un'azienda o un team e devi far quadrare i conti: la sovraingegnerizzazione, come quella che deriva dall'applicazione dogmatica della UI composition dove non serve, non è un dettaglio tecnico per addetti ai lavori. È un problema di business. È un costo pesante che finisci per pagare tu, in modi diversi ma tutti dannosi.
Il costo più immediato e facile da capire è il maggior tempo di sviluppo.
Se per realizzare una funzionalità che, con un approccio semplice, richiederebbe 5 giorni di lavoro, il team ce ne mette 15 perché deve seguire i dettami di un'architettura iper-complessa, quei 10 giorni di differenza sono un costo vivo per te.
Sono ore di sviluppo che paghi per produrre complessità tecnica, non valore funzionale diretto per i tuoi utenti o per il tuo business.
Moltiplica questo per l'intera applicazione e capisci come i budget possano lievitare senza un reale motivo.
È come pagare un architetto per progettare una cuccia del cane con le stesse tecniche e gli stessi materiali di un grattacielo: tecnicamente impressionante, ma uno spreco assurdo di risorse.
Poi c'è il costo della minore qualità e dei bug aggiuntivi.
Non è vero che un software più complesso è automaticamente migliore.
Anzi, è quasi sempre il contrario.
Più codice c'è, più strati di astrazione ci sono, più interazioni intricate tra le parti mobili, più è facile che qualcosa si rompa, specialmente in modi subdoli e difficili da prevedere.
I bug in sistemi sovraingegnerizzati sono spesso più difficili da diagnosticare e risolvere.
Questo significa più tempo speso in debugging (altri costi), rilasci meno affidabili, e maggiori disagi per chi usa il software (che potresti essere tu stesso o i tuoi clienti).
Un software più semplice è quasi sempre un software più robusto e affidabile.
Non dimenticare il costo nascosto ma pesantissimo della manutenzione futura.
Un'applicazione costruita su fondamenta inutilmente complesse diventa un fardello negli anni.
Ogni volta che devi aggiungere una nuova funzionalità, adattare il software a nuove normative, o semplicemente correggere un comportamento, l'intervento è più lungo, più rischioso e più costoso.
Se lo sviluppatore o il piccolo gruppo che aveva in testa quell'architettura specifica se ne va, potresti trovarti in guai seri, con un sistema che nessuno capisce più a fondo e su cui è difficile intervenire.
È il classico "debito tecnico": hai risparmiato (forse!) un po' di pensiero critico all'inizio scegliendo la soluzione più "alla moda", e ora paghi gli interessi per anni sotto forma di manutenzione lenta e costosa.
Infine, considera il costo umano: la frustrazione del team.
Gli sviluppatori bravi amano risolvere problemi complessi, ma odiano combattere contro complessità artificiale e inutile.
Lavorare su un sistema farraginoso, dove anche le modifiche semplici richiedono giri pindarici, è demotivante.
Porta a minore produttività, a stress, e può aumentare il rischio che le persone migliori cerchino lidi più sereni (e produttivi).
E trovare sostituti capaci di districarsi in quella complessità specifica sarà più difficile e costoso.
Quindi, la prossima volta che ti propongono una soluzione tecnica che suona molto complessa e "avanzata", fai la domanda fondamentale:
Ok, bello, ma quali sono i benefici concreti per noi, per il nostro budget, per la nostra capacità di mantenere e far evolvere questo software in futuro?
Questa complessità è giustificata dal problema che dobbiamo risolvere?
Se le risposte sono vaghe o troppo accademiche, alza le antenne.
Potresti star per pagare il prezzo della sovraingegnerizzazione.
Cosa funziona davvero nei progetti desktop WPF e MAUI in Italia

Dopo aver analizzato in modo critico quello che non funziona per la maggior parte dei casi, è giusto concentrarsi su quello che invece funziona.
Qual è l'approccio che, nella mia esperienza, permette a team piccoli e medi di costruire ottime applicazioni desktop, solide, manutenibili e consegnate senza far esplodere i costi?
Non è una formula magica o l'ultima tecnologia scintillante.
È un ritorno ai fondamentali, all'ingegneria del software pragmatica, focalizzata sulla semplicità e sulla chiarezza.
Ecco gli ingredienti che vedo dare i risultati migliori, quelli che consiglio sempre ai miei clienti:
Organizzazione logica chiara
Il punto fondamentale è separare nettamente l'aspetto visivo dell'interfaccia (come appare una finestra) dalla sua logica di comportamento (cosa succede quando clicchi un bottone, quali dati mostrare).
Ci sono vari modi per farlo, ma l'importante è il principio: tieni separato il codice della grafica da quello della logica sottostante e dai dati di business.
Questa separazione rende tutto più comprensibile e gestibile, senza bisogno di complicazioni inutili.
Un progetto, tanta organizzazione interna
Per la stragrande maggioranza delle applicazioni, non serve dividere il codice in decine di progetti separati.
Un unico progetto principale, ma organizzato in modo impeccabile al suo interno con cartelle specifiche (una per le finestre, una per la logica associata, una per i modelli dei dati, una cruciale per i "Servizi" di business, una per le utilità condivise...), è molto più facile da gestire, compilare e distribuire.
L'ordine si fa con la disciplina interna, non con la frammentazione tecnica fine a se stessa.
Separazione ferrea delle responsabilità
Questo è un pilastro irrinunciabile.
La logica che gestisce una finestra NON deve MAI contenere codice che parla direttamente col database, o che implementa regole di business complesse (come il calcolo di un preventivo).
Per semplificare, queste logiche importanti devono stare in classi dedicate, che sono totalmente indipendenti dall'interfaccia grafica.
La logica della finestra usa questi servizi per ottenere dati o eseguire operazioni.
Così, se cambia una regola di business, la modifichi in un solo posto, e puoi testare quella regola indipendentemente dall'interfaccia.
Controlli utente personalizzati? Con giudizio!
Le tecnologie per creare interfacce desktop ti permettono di creare "pezzi" riutilizzabili.
Ma non abusarne.
Ha senso creare un controllo personalizzato solo se hai un pezzo di interfaccia veramente complesso (non un semplice campo di testo, ma magari un editor grafico, un visualizzatore di mappe interattivo...) che devi riutilizzare identico in molte finestre diverse e che ha una sua logica interna complessa.
Per tutto il resto, usa i controlli standard che la tecnologia ti offre.
Meno codice personalizzato scrivi, meno ne devi mantenere e meno rischi di introdurre bug.
Librerie esterne: il vero riuso intelligente
Se ti serve una griglia dati con un milione di funzionalità, un sistema di grafici professionali, un modo per generare report complessi o esportare in PDF/Excel, NON metterti a costruirli da zero!
Sarebbe una follia costosa e probabilmente non arriveresti mai alla qualità dei componenti specializzati disponibili sul mercato.
Esistono ottime librerie di componenti (alcune a pagamento, altre gratuite) che ti danno queste funzionalità pronte all'uso, testate e ottimizzate.
Scegliere le librerie giuste e integrarle bene nel tuo progetto è il modo più intelligente ed economico per ottenere funzionalità avanzate senza reinventare la ruota.
Questo è riuso che porta valore reale al tuo progetto e al tuo budget.
Questo approccio, basato sul pragmatismo e semplicità, non fa gridare all'ultima moda tecnologica, ma ti assicuro che è quello che porta a casa il risultato: software che funziona, consegnato nei tempi e nei costi, e che può essere mantenuto e fatto evolvere negli anni senza diventare un incubo.
È l'approccio che rispetta il tuo investimento e la professionalità di chi ci lavora.
Quando (e solo quando) ha senso usare la UI composition in WPF/MAUI

Permettimi di ripeterlo ancora perché è davvero il cuore del discorso: nel 99% dei progetti di applicazioni desktop che probabilmente stai gestendo o sviluppando (il gestionale per la produzione, l'app per l'ufficio tecnico, il software per controllare un macchinario), la UI composition spinta, quella che richiede framework complessi e architetture a moduli separate, è la scelta sbagliata.
È come usare la motosega per pelare una mela: uno spreco di energia e un rischio inutile, che fa solo danni.
Esiste però quell'1% di casi, situazioni molto particolari, in cui la motosega serve davvero.
È importante saperle riconoscere per non applicare la stessa logica a contesti completamente diversi.
Non si tratta di sfumature, ma di differenze abissali nella scala, negli obiettivi e nell'organizzazione del lavoro.
Vediamo nel dettaglio questi scenari eccezionali:
Applicazioni GIGANTESCHE di livello enterprise globale
Qui non parlo del gestionale, per quanto complesso, della tipica azienda italiana.
Parlo di software che sono il cuore operativo di multinazionali, banche d'affari globali, compagnie aeree internazionali.
Sistemi con centinaia o migliaia di schermate, logiche di business interconnesse su scala planetaria, requisiti di sicurezza e affidabilità estremi.
Per sviluppare e mantenere mostri del genere, servono team enormi (centinaia di sviluppatori) distribuiti in tutto il mondo.
In questo contesto, una decomposizione fisica molto spinta in moduli indipendenti, gestita con framework di composition e processi rigorosi, diventa una necessità vitale per poter gestire la complessità, coordinare il lavoro di tanti team, isolare le modifiche e garantire la stabilità del sistema.
La modularità qui non è un vezzo tecnico, ma una risposta obbligata a una scala operativa e organizzativa fuori dal comune.
Piattaforme progettate per essere estese da plugin di terze parti
Questo è un modello di business specifico.
Stai creando un software che non è "finito", ma è una piattaforma aperta, progettata apposta perché altre aziende o sviluppatori possano creare e aggiungere le proprie funzionalità (i "plugin" o "estensioni").
Pensa a un software di grafica a cui puoi aggiungere nuovi filtri o pennelli, o a un ambiente di sviluppo a cui puoi aggiungere supporto per nuovi linguaggi.
Per permettere questo, l'architettura deve essere "aperta" e prevedere meccanismi standard per caricare codice esterno (spesso distribuito come file separati) e integrarlo sia a livello logico che a livello di interfaccia utente.
Qui, tecniche di composition e caricamento dinamico sono indispensabili per la natura stessa del prodotto.
Ma se stai costruendo un'applicazione "chiusa" per un cliente specifico o per uso interno, questa esigenza non esiste.
Software venduto come prodotto standard ma con esigenze estreme di personalizzazione
Immagina di avere un prodotto software che vendi a decine di clienti diversi in white label o multi tenant, ma ogni cliente non vuole solo il suo logo, vuole un'esperienza quasi unica.
Vuole poter attivare o disattivare interi moduli funzionali (es. l'e-commerce sì, la gestione magazzino no), vuole aggiungere campi dati personalizzati che appaiano automaticamente nelle maschere e nei report, vuole magari un flusso operativo completamente diverso per alcune procedure chiave.
Per gestire una variabilità così spinta senza dover mantenere decine di versioni diverse del software, potresti aver bisogno di un'architettura molto flessibile, dove l'interfaccia stessa viene "assemblata" al momento in base alla configurazione del cliente (tenant), caricando i pezzi giusti e adattando il layout.
È una sfida tecnica notevole, giustificata solo da un modello di business che richiede questo livello estremo di personalizzazione su larga scala.
Le normali personalizzazioni si gestiscono in modi molto più semplici.
Gestione di team di sviluppo grandi (>30/40 Persone) e distribuiti geograficamente
A volte, la scelta di un'architettura a moduli separati è dettata più da esigenze organizzative che tecniche intrinseche dell'applicazione.
Se hai un team di sviluppo molto numeroso, magari sparso in diverse sedi o che lavora con metodologie che richiedono forte parallelizzazione, dividere l'applicazione in moduli può sembrare un modo per ridurre le interferenze tra i sotto-team e facilitare il lavoro indipendente.
Con la mia lunga esperienza ti suggerisco cautela: se i moduli non sono logicamente ben separati e indipendenti, questa divisione fisica può creare più problemi di comunicazione e integrazione di quanti ne risolva.
Spesso è meglio investire in buone pratiche di gestione del codice sorgente e comunicazione del team piuttosto che in un'architettura UI artificialmente complessa.
È una soluzione da considerare solo in casi di team veramente grandi e solo se accompagnata da processi organizzativi adeguati.
Come puoi facilmente capire, si tratta di situazioni molto lontane dalla quotidianità della maggior parte delle aziende e delle software house italiane.
Se il tuo progetto non rientra palesemente in uno di questi scenari limite, allora la conclusione più logica, saggia ed economicamente vantaggiosa è tenerti alla larga dalla UI composition spinta.
Non è la soluzione ai tuoi problemi, rischierebbe solo di crearne di nuovi e più costosi.
Esempio pratico: come rendere semplice un’UI desktop senza incastrarti nei pattern inutili

Ti voglio rendere ancora più concreta la differenza tra i due mondi.
Pensa di nuovo di dover realizzare la finestra per modificare i dati di un cliente.
Non ti mostrerò righe di codice, ma ti descriverò cosa implica per te, per il tuo team e per il tuo portafoglio scegliere una strada o l'altra.
Se imbocchi la strada della sovraingegnerizzazione (UI composition spinta)
- L'inizio del progetto sarà lento e costoso, perché gran parte del tempo iniziale verrà speso a impostare l'architettura complessa, a scegliere e configurare librerie specifiche, a definire moduli e interfacce astratte, prima ancora di produrre funzionalità visibili.
- Lo sviluppo di ogni singola finestra richiederà più tempo del necessario, perché bisognerà spezzettare la logica e l'interfaccia in tanti piccoli componenti separati e poi scrivere altro codice "collante" per farli funzionare insieme in modo coordinato.
- Il codice sorgente diventerà molto più voluminoso, frammentato e difficile da capire nel suo insieme, richiedendo più tempo per il debug e per l'inserimento di nuove persone nel team.
- La probabilità di introdurre bug subdoli, legati alle interazioni complesse tra i componenti o al ciclo di vita dell'architettura, sarà più alta.
La manutenzione futura (aggiungere campi, modificare logiche, correggere errori) sarà più lenta, più rischiosa e quindi più costosa.
Richiederà personale con competenze specifiche su quell'architettura.
- Il costo totale per sviluppare e mantenere l'applicazione sarà significativamente più elevato.
Se invece scegli la strada del pragmatismo (semplicità e buona organizzazione):
L'inizio del progetto sarà rapido.
Puoi partire quasi subito a sviluppare le funzionalità richieste dal cliente, concentrandoti sul valore di business.
Lo sviluppo di ogni finestra è più veloce e diretto.
La logica è concentrata e più facile da implementare e testare.
Il codice sorgente sarà più compatto, più facile da leggere, capire e navigare, anche per chi non l'ha scritto.
- Il debugging sarà più rapido ed efficace, perché i potenziali punti di errore saranno più limitati e facili da isolare.
La manutenzione futura sarà più agile ed economica.
Le modifiche richiederanno meno tempo e potranno essere fatte anche da sviluppatori con competenze standard.
- Il costo totale per sviluppare e mantenere l'applicazione sarà decisamente inferiore.
Facciamo un riepilogo per mettere a confronto quando i due approcci hanno senso.
Scenario | Quando (forse) ha senso usare la UI Composition |
---|---|
Applicazioni enterprise globali con requisiti estremi | Progetti mastodontici con centinaia di schermate e team distribuiti in tutto il mondo. Qui la UI composition spinta serve davvero per coordinare il lavoro, mantenere la scalabilità e gestire la complessità. |
Piattaforme estensibili con plugin di terze parti | Se stai creando un software pensato per essere arricchito da estensioni esterne, la UI composition diventa fondamentale per supportare un’architettura modulare e caricabile dinamicamente. |
Prodotti multi-tenant con personalizzazioni estreme | Quando vendi lo stesso software a decine di clienti diversi, ciascuno con esigenze funzionali e visive molto diverse, ti serve un sistema flessibile per comporre l’interfaccia su misura a runtime. |
Team numerosi e distribuiti geograficamente | Se il tuo team supera le 30-40 persone e lavora in sedi diverse, puoi valutare la divisione in moduli per ridurre le interferenze. Ma attenzione: solo se i moduli sono davvero indipendenti e i processi interni sono maturi. |
Il punto è questo: a parità di risultato funzionale per l'utente finale, un approccio ti costa molto di più dell'altro in termini di tempo, denaro, rischi e stress.
La scelta "tecnologicamente avanzata" non è sempre la scelta migliore per il business.
Anzi, quasi mai lo è quando si parla di applicare architetture complesse a problemi che non le richiedono.
Come responsabile di un progetto o di un'azienda, il tuo obiettivo dovrebbe essere ottenere il massimo valore con il minimo spreco di risorse.
Scegli la semplicità, l'organizzazione pulita e gli strumenti giusti per il lavoro specifico, è la via maestra per raggiungere questo obiettivo.
Non farti sedurre dalla complessità fine a se stessa.