La maggior parte dei corsi di architettura Angular insegnano la teoria. Non questo.
Qui impari a individuare problemi reali, prendere decisioni mirate e costruire sistemi che durano quando il team cresce, il prodotto cambia e il tempo passa.
Venti moduli. Orientato alla pratica. Nessuna imbottitura.
INDICE — Architettura angolare pratica per la vita reale
01 — Problemi architettonici di base
- Logica di business nei componenti
- Servizi di Dio
- Stato duplicato o sparso
- Accoppiamento tra caratteristiche
- Responsabilità miste
- Cartelle dall'aspetto ordinato ma non ridimensionabili
- Astrazioni premature
- Ingegneria eccessiva e inutile
- Architettura pensata per l’oggi ma non per la crescita
- Codice difficile da eliminare, spostare o rifattorizzare
02 — Struttura effettiva del progetto
- Basato su funzionalità o basato su livelli
- Quando utilizzare ciascun approccio
- Come rilevare una struttura che non è in scala
- Come dividere per domini o contesti delimitati
- Come organizzare le cartelle per i team reali
- Convenzioni di denominazione utili
- Cosa mettere e cosa NON mettere
shared - Come verificare se la tua struttura attuale supporta ×10
03 — Architettura dei componenti
- Componenti troppo grandi
- Componenti con troppe responsabilità
- Componenti intelligenti e stupidi
- Contenitore/presentativo in Angular moderno
- Composizione dei componenti
- Quando riutilizzare e quando NON
- Come progettare API di componenti pulite
- Ingressi/uscite vs segnali vs servizi
- Problemi tipici nei modelli
- Come rilevare componenti di difficile manutenzione
04 — Stato immobiliare in Angolare
- Quali tipi di stato esistono
- Come individuare l'abuso di Stato
- Stato locale, condiviso, globale e server
- Segnali vs RxJS
- Posizionamento statale
- Quando utilizzare lo stato del servizio
- Quando utilizzare i negozi di segnali
- Quando utilizzare NgRx
- Come rilevare l'eccessiva centralizzazione
- Come rilevare il caos distribuito
- Errori tipici negli effetti collaterali
- Normalizzazione dello Stato
- Lista di controllo dello stato
05 — Comunicazione e flusso di dati
- Dati giù/eventi su
- Corretta comunicazione tra i componenti
- Comunicazione errata tra i componenti
- Comunicazione tra caratteristiche
- Segni di accoppiamento nascosto
- Usare i servizi per comunicare
- Usare i segnali per comunicare
- Quando un bus per eventi è una cattiva idea
- Come controllare gli indirizzi delle dipendenze
- Come rilevare un flusso di dati difficile da seguire
06 — Livello dati e accesso API
- Servizi vs repository
- DTO vs modelli
- Trasformazioni e mappature
- Dove mettere gli adattatori
- Errori tipici durante l'utilizzo delle API
- Come disaccoppiare il frontend dal backend
- Nuovo tentativo, memorizzazione nella cache, impaginazione
- Scorrimento infinito
- REST vs GraphQL dall'architettura
- Come verificare se il livello dati è pulito o misto
07 — Architettura di instradamento
- Progettare percorsi con intenzione
- UX + SEO nei percorsi
- Percorsi pigri
- Guardie
- Risolvere
- URL come origine dello stato
- Collegamento profondo
- Percorsi accoppiati
- Percorsi mal studiati
- Lista di controllo per rivedere la navigazione e la scalabilità
08 — Rendering e strategia globale
- SPA, SSR, SSG, ISR
- Come scegliere in base al contesto
- SEO vs complessità
- Prestazioni rispetto ai costi
- Architettura SSR angolare
- Idratazione e impatto architettonico
- Quando NON utilizzare SSR
- Come verificare se un'app necessita di una strategia di rendering diversa
09 — Architettura delle prestazioni
- Rilevamento modifiche, OnPush, segnali e prestazioni
- Caricamento lento reale
- Suddivisione del codice e strategia bundle
- Memorizzazione nella cache
- Scorrimento virtuale e memorizzazione
- Budget di prestazione
- Come rilevare i colli di bottiglia architettonici
- Cosa controllare prima di "ottimizzare"
- Come distinguere un problema di codice da un problema di architettura
10 — Testare l'architettura
- Cosa testare e cosa no
- Unità vs integrazione vs e2e
- Testabilità in base alla progettazione
- Come rilevare il codice difficile da testare
- Beffardo con significato
- Fragilità nel testare e nel sovratestare
- Frontend-backend per test del contratto
- Lista di controllo per verificare se un'architettura favorisce o interrompe i test
11 — Nx e monorepo reale
- Quando ne vale la pena e quando no
- App vs librerie, confini, grafico delle dipendenze
- Librerie condivise ben realizzate e mal realizzate
- Interessato, memorizzazione nella cache, proprietà del codice
- Come adattarsi a più team
- Anti-pattern Monorepo
- Esamina la lista di controllo
12 — Microfrontend e federazione di moduli
- Quando sì e quando no
- Quale problema reale risolvono?
- Costi nascosti
- Host vs remoti, controllo delle versioni, comunicazione tra MFE
- Distribuzione indipendente e complessità organizzativa
- Lista di controllo per decidere se paga
13 — Sistemi di progettazione utili
- Quale problema risolvono e quando non ne serve uno serio?
- Librerie di componenti, token, tematiche, varianti
- Coerenza vs flessibilità
- Libro di fiabe, sincronizzazione progettazione-sviluppo
- Errori tipici
- Come verificare se il tuo sistema di interfaccia utente si sta ridimensionando o si blocca
14 — Sicurezza del frontend
- XSS, CSRF, sanificazione
- Architettura di autenticazione: JWT, token di aggiornamento
- Guardie e accesso basato sui ruoli
- Problemi di sicurezza nella SSR
- Lista di controllo pratica per la revisione
15 — Osservabilità e manutenzione
- Registrazione, tracciamento degli errori, Sentry, metriche
- Tracciamento concettuale, flag di funzionalità, test A/B
- Come rilevare gli angoli ciechi
- Come verificare se un'app è utilizzabile in produzione
16 — DevEx e pensiero basato sulla piattaforma
- Esperienza e strumenti dello sviluppatore
- CI/CD, generatori, schemi, automazione
- Come rilevare l'attrito non necessario
- Come progettare l'architettura per i team e non solo il codice
17 — Compromessi e processo decisionale
- Come giustificare le decisioni
- Costi vs benefici, complessità vs scalabilità, velocità vs manutenibilità
- Costruisci o acquista
- Come scrivere le ADR
- Come difendere una decisione in un colloquio o in un lavoro reale
18 — Anti-modelli dell'architetto angolare
- Eccessiva ingegneria, stratificazioni inutili, astrazioni premature
sharedStato globale mal progettato e non necessario- Dipendenze incrociate, accoppiamento silenzioso
- Cattiva modularizzazione, ignora le metriche reali
- Lista di controllo delle bandiere rosse
19 — Audit pratico delle app Angular
- Come rivedere un'app esistente
- Cosa guardare per primo
- Quali segni indicano un debito serio
- Come dare priorità ai problemi
- Cosa aggiustare prima e cosa non toccare ancora
- Come fare una revisione architettonica utile
20 — Casi reali e formazione
- Controllo dell'e-commerce
- Controllo del dashboard SaaS
- Audit del back office aziendale
- Audit delle app pubbliche con SEO
- Progettazione dell'app da zero
- Riprogettazione caotica dell'app
- Domande dell'intervista
- Esercizi di rilevazione, decisione e miglioramento
Modulo 0 — Base del sistema
Il punto 0 non è un modulo di contenuto. È il modo di vedere che utilizzerai durante tutta la tabella di marcia.
Prima di parlare di problemi specifici in Angular è necessario rispondere ad una domanda:
Come guardi un'app che non conosci e decidi se è buona o cattiva?
Ci sono quattro abilità di base per questo.
1. Analizza un'app senza toccare il codice
La prima lettura di un'app non è nell'editor. E' nella struttura. Prima di aprire un singolo file, puoi estrarre le informazioni:
- Come si chiamano le cartelle? I nomi dicono cosa fanno?
- Esiste una cartella
sharedche pesa più di tutto il resto? - Quanti livelli di nidificazione ha la struttura?
- I moduli o le funzionalità hanno nomi di dominio o nomi tecnici?
- Dove sono i servizi? Sciolto o entro le caratteristiche?
Già questo dice molto se chi ha realizzato l'app ha pensato in termini di business o in termini di livelli tecnici.
2. Rivedere l'architettura in modo pratico
Revisionare non significa leggere il codice dall'alto verso il basso. Si tratta di porre domande con criteri:
- Dove vive la logica aziendale?
- Chissà cosa in ogni strato?
- Quanti siti devi modificare per aggiungere una nuova funzionalità?
- Ci sono dipendenze che vanno nella direzione sbagliata?
Un architetto senior non inizia con la componente più complessa. Inizia con i punti di rischio più elevati: servizi condivisi, stato globale e livello di accesso ai dati.
3. Rileva i problemi prima della programmazione
La maggior parte dei danni architettonici si verifica nelle prime decisioni che sembrano irrilevanti:
- "Per ora lo metto su un servizio condiviso"
- "Il componente principale gestisce questo stato, quindi lo spostiamo"
- "Utilizziamo NgRx per tutto, quindi è coerente"
Queste decisioni hanno conseguenze reali mesi dopo. Il criterio architettonico è sapere cosa acquista ciascuna decisione e quale debito sta contraendo.
4. Converti la teoria in una lista di controllo reale
Ogni concetto che vedremo in questa tabella di marcia (componenti intelligenti/stupidi, posizionamento statale, servizi divini, ecc.) deve terminare con una domanda che puoi farti mentre rivedi il codice reale:
- Questo componente sa da dove provengono i dati?
- Questo servizio ha più di un motivo per cambiare?
- Questo stato esiste in due luoghi diversi?
Se non riesci a trasformare un concetto in una domanda di revisione, non l'hai ancora interiorizzato.
Modulo 1: come analizzare un'app Angular senza toccare il codice
La prima revisione dell'architettura avviene prima dell'apertura dell'editor. Già solo dalla struttura delle cartelle e dai nomi dei file si possono ricavare chiari segnali di qualità. Questo è ciò che farebbe un architetto senior nei primi 10 minuti con un'app sconosciuta.
Perché è importante?
- Se si impiega molto tempo per rilevare i problemi architetturali, il costo per correggerli aumenta in modo esponenziale.
- Una struttura mal progettata dal primo giorno diventa debito tecnico che blocca la squadra mesi dopo.
- Lo sviluppo di un rapido giudizio visivo ti consente di prendere decisioni migliori prima di scrivere una singola riga di codice.
Passaggio 1: leggi la struttura delle cartelle come una mappa
La struttura delle cartelle è la prima decisione architettonica visibile. Ti racconta come il team ha organizzato il proprio mondo mentale: pensano in termini di tecnologia o in termini di business?
Che cos'è una funzionalità?
Una caratteristica è una funzionalità aziendale completa. Non un tipo di file. Non uno strato tecnico.
Pensa a un'app di e-commerce. Le caratteristiche sono:
- Il catalogo prodotti
- Il carrello della spesa
- Il processo di pagamento
- Il profilo utente
- Gestione degli ordini
Ognuno di questi è un business che ha senso da solo. Un utente entra, sfoglia il catalogo, aggiunge al carrello, effettua il checkout. Queste sono le caratteristiche.
Modello 1: organizzazione per livelli tecnici (problematico su larga scala)
Questo è ciò che fanno quasi tutti quando iniziano perché sembra carino:
src/app/
components/
product-card.component.ts
product-list.component.ts
cart-item.component.ts
checkout-form.component.ts
user-profile.component.ts
services/
product.service.ts
cart.service.ts
checkout.service.ts
user.service.ts
models/
product.model.ts
cart.model.ts
user.model.ts
Il vero problema: devi modificare il flusso di pagamento. Per capire di cosa si tratta, navighi tra tre diverse cartelle: cerchi il componente in components/, il servizio in services/, il modello in models/. Se è presente una pipe di pagamento specifica, è in pipes/ mescolata con pipe di altre funzionalità.
Per modificare una singola funzionalità, navighi nell'intera app. Questo è l'accoppiamento per struttura. Quando il team cresce, due persone che lavorano su funzionalità diverse toccano costantemente le stesse cartelle → conflitti in git, difficoltà a sapere chi possiede cosa.
Modello 2: organizzazione per caratteristiche (quale scala)
src/app/
features/
catalog/
components/
product-card.component.ts
product-list.component.ts
services/
product.service.ts
models/
product.model.ts
catalog.routes.ts
cart/
components/
cart-item.component.ts
cart-summary.component.ts
services/
cart.service.ts
cart.routes.ts
checkout/
components/
checkout-form.component.ts
order-confirmation.component.ts
services/
checkout.service.ts
checkout.routes.ts
shared/
core/
Perché funziona: quando tocchi Checkout, tutto in Checkout è insieme. Un nuovo sviluppatore sa esattamente dove cercare. Una persona può possedere un'intera funzionalità. Puoi eliminare un'intera funzionalità eliminando solo la relativa cartella. I conflitti Git tra le diverse funzionalità scompaiono quasi completamente.
La regola pratica chiave: se elimini una funzione, puoi eliminare l'intera cartella senza toccare nient'altro? Se la risposta è sì, la caratteristica è ben isolata. Se devi cercare pezzi nell'app, non lo è.
Passaggio 2: leggi i nomi
I nomi sono documentazione. Un brutto nome nasconde le intenzioni e aggiunge carico cognitivo a ogni persona che legge il codice.
shared/ — cosa dovrebbe contenere e cosa non dovrebbe
shared/ è per cose che utilizzano più funzionalità e che NON hanno una propria logica aziendale.
Correggi in shared/:
shared/
components/
button/
modal/
spinner/
avatar/
pipes/
date-format.pipe.ts
truncate.pipe.ts
directives/
click-outside.directive.ts
validators/
email-validator.ts
Sono pezzi di interfaccia utente riutilizzabili o pure utilità. Non sanno nulla del business. A ButtonComponent non sa se si tratta di un pulsante di pagamento o di un profilo utente. Sa solo essere un bottone.
Errato in shared/:
shared/
services/
cart.service.ts ← lógica de negocio aquí
user-auth.service.ts ← pertenece a auth/core
product-filter.service.ts ← pertenece a catalog
report-generator.service.ts ← pertenece a reporting
Quando vedi servizi con logica di business all'interno di shared/, significa che qualcuno non sapeva dove metterli e li ha messi lì. Con il passare del tempo shared/ diventa il punto di riferimento dell'app.
core/ — cos'è e cosa non è
core/ è per l'infrastruttura singleton che necessita dell'intera app, una volta.
Correggi in core/:
core/
services/
auth.service.ts
logger.service.ts
error-handler.service.ts
interceptors/
auth.interceptor.ts
error.interceptor.ts
guards/
auth.guard.ts
models/
user-session.model.ts
Errato in core/:
core/
components/
dashboard.component.ts ← eso es una feature
home.component.ts ← igual
services/
product.service.ts ← pertenece a catalog
report-generator.service.ts ← pertenece a reporting
Queste sono cose che vengono istanziate una volta e influenzano l'intera app. L'intercettatore di autenticazione intercetta tutte le richieste HTTP per l'intera app, motivo per cui risiede in core/.
Nomi vaghi: segnali d'allarme
Un nome vago è una decisione rinviata. Quando qualcuno crea data.service.ts, non ha deciso di quali dati parla il servizio.
| quello che vedi | Cosa può significare |
|---|---|
shared/ molto grande |
Tutto ciò che non andava bene da nessun'altra parte. Diventa un miscuglio. |
common/ |
Uguale a shared/, ma con un nome peggiore. Senza criteri su ciò che entra. |
utils/ con servizi |
Logica aziendale mascherata da utilità. Grave bandiera rossa. |
helpers/ |
Uguale a utils/. Il nome non dice nulla sulla responsabilità. |
components/ nella radice |
Nessuna organizzazione per dominio. Tutti i componenti insieme. |
core/ con 40 file |
Nucleo poco definito. shared/ è stato utilizzato come secondo. |
app.service.ts |
Un servizio divino in attesa di crescere. Segnale di allarme massimo. |
data.service.ts |
Dati su cosa? Responsabilità indefinita dal nome. |
helper.service.ts |
Finisce per essere un servizio con metodi non correlati. |
main.component.ts |
Cos'è "principale"? Nome generico che nasconde una vera responsabilità. |
Regola del nome: Se il nome del file non ti dice esattamente cosa fa, probabilmente il file fa troppe cose o è mal posizionato.
Esempi concreti di nomi vaghi e cosa nascondono:
utils/format.tscon 800 righe: mix di formattazione della data + convalida del modulo + trasformazione API.helper.service.ts: finisce per essere un servizio divino con metodi non correlati.app.service.ts: un servizio chiamato l'intera app che ha la responsabilità dell'intera app.
Passaggio 3: conta e misura senza aprire i file
Prima di leggere il codice potete fare queste osservazioni direttamente dalla struttura:
Dimensione del componente come segno
| Cartello | Cosa indica? |
|---|---|
| +300-400 linee in un componente | Quasi sempre stai facendo troppo. Non è una regola assoluta ma vale la pena approfondire. |
| +10 ingressi e uscite | API troppo complessa. Candidato da suddividere in più componenti. |
| Chiamate HTTP dirette nel componente | Combina la presentazione con l'accesso ai dati. Livello di servizio mancante. |
| 1-2 componenti per funzione | Probabilmente stanno facendo troppo. Mancanza di divisione interna. |
| +20 fornitori globali | Troppa logica statale e centralizzata. Non tutto deve essere globale. |
Servizi globali: il pericolo di providedIn: 'root'
Un servizio globale significa che qualsiasi componente di qualsiasi funzionalità può inserirlo e utilizzarlo. Ciò crea dipendenze invisibili tra funzionalità che dovrebbero essere indipendenti.
// Servicio que DEBERÍA ser local a la feature de catalog:
@Injectable({ providedIn: 'root' }) // ← señal de problema
export class ProductFilterService { ... }
// Ahora cualquier componente de cualquier feature puede usarlo.
// Si catalog cambia su lógica de filtrado, puede romper
// algo en una feature aparentemente no relacionada.
Passaggio 4: app.module.ts o app.config.ts: cosa cercare
Nelle app con moduli (Angular < 17 o legacy)
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
RouterModule,
AuthModule, // ← bien, infraestructura singleton
ProductModule, // ← señal de problema
CartModule, // ← señal de problema
CheckoutModule, // ← señal de problema
UserModule, // ← señal de problema
DashboardModule, // ← señal de problema
// Si hay 15+ módulos de features aquí,
// el lazy loading no está funcionando.
],
providers: [
ProductService, // ← si hay 20+ providers aquí,
CartService, // hay demasiada centralización
UserService,
AuthService,
]
})
I moduli funzionalità dovrebbero essere caricati in modo lento (su richiesta), non importati direttamente nel modulo root. Se sono tutti qui, vengono tutti caricati all'avvio anche se l'utente non ne ha mai bisogno.
Nelle app autonome (Angular 17+)
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes, withLazyLoading()), // ← correcto
provideHttpClient(withInterceptors([authInterceptor])),
provideAnimations(),
ProductService, // ← esto NO debería estar aquí
CartService, // ← pertenece a la feature de cart
]
}
Regola di configurazione globale: la configurazione globale dovrebbe avere solo un'infrastruttura che interessa l'intera app: router con caricamento lento,
HttpClientcon intercettori globali, animazioni, servizi singleton dell'infrastruttura (autenticazione, logger, gestore degli errori). Se vedi servizi di funzionalità nella configurazione globale, qualcuno sta registrando cose a livello globale per comodità, non per necessità.
Checklist completa: prima lettura di un'app Angular
Struttura
- La struttura è organizzata per caratteristiche (dominio aziendale) o per livelli tecnici?
- Posso eliminare un'intera funzione eliminando solo la sua cartella senza toccare nient'altro?
- Le funzionalità hanno nomi di dominio aziendali (
checkout,catalog) o nomi tecnici (list,form,detail)? - Sono presenti cartelle con nomi vaghi:
utils,helpers,common,misc,data?
shared/ e core/
-
shared/contiene solo componenti e utilità dell'interfaccia utente senza logica aziendale? -
core/contiene solo l'infrastruttura singleton (intercettori, guardie, servizio di autenticazione)? - Sono presenti servizi con logica aziendale all'interno di
shared/? -
core/dispone di componenti di funzionalità o servizi di dominio?
Servizi e fornitori
- I servizi aziendali sono all'interno delle vostre funzionalità o sciolti a livello globale?
- Esistono più di 20 fornitori globali?
- Il modulo root o
app.config.tsimporta direttamente i moduli delle funzionalità (senza lazy)? - Esistono servizi con
providedIn: 'root'che dovrebbero essere locali per una funzione?
Nomi
- I nomi dei file dicono esattamente cosa fanno?
- Esistono file il cui nome potrebbe applicarsi a qualsiasi parte dell'app?
- Esiste un file denominato
app.service.ts,data.service.tsohelper.service.ts? -
shared/pesa più di ogni singola caratteristica?
Esercizio
Cerca qualsiasi progetto Angular pubblico su GitHub: può essere tuo o open source. Senza aprire alcun file, osservando semplicemente la struttura e i nomi delle cartelle:
- Quale modello organizzativo utilizzi: funzionalità o livelli tecnici?
- Quali nomi ti danno dubbi o sospetti?
- Dove sospetti che sia la logica aziendale?
- Quale cartella pensi abbia il maggior mix di responsabilità?
- Potresti eliminare una funzione senza toccare nient'altro?
Condividi ciò che osservi durante la sessione e lo esamineremo insieme come farebbe un architetto senior.
Suggerimento: analizza il tuo progetto con i 4 punti
Copia questo prompt e usalo con qualsiasi AI (ChatGPT, Claude, Copilot) passandogli l'albero delle cartelle del tuo progetto:
Analizza l'architettura del progetto applicando questi 4 punti del Pilastro 1 e genera un report.
PUNTO 1 — Struttura delle cartelle
Analizza apps/ e libs/ e rispondi:
- L'organizzazione è per caratteristiche (dominio aziendale) o per livelli tecnici?
- Puoi eliminare un'intera funzionalità eliminando solo la sua cartella?
- Le funzionalità hanno nomi di dominio (
checkout,catalog) o nomi tecnici (list,form,detail)? - Sono presenti cartelle con nomi vaghi:
utils,helpers,common,misc,data? - Mostra l'albero reale delle cartelle che hai trovato.
PUNTO 2 — Nomi di file e cartelle
Cerca nell'intero progetto ed elenca:
- File con nomi vaghi:
data.service.ts,helper.service.ts,app.service.ts,main.component.ts,utilscon servizi all'interno. - Servizi con logica aziendale all'interno di
shared/. - Componenti o servizi di dominio all'interno di
core/. shared/pesa più di ogni singola caratteristica? Conteggio file.
PUNTO 3 — Servizi globali
Cerca nell'intero progetto:
- Tutti i servizi con
providedIn: 'root': elenca quali sono e se devono essere locali per una funzione. - Conta quanti fornitori globali ci sono in totale.
- Identifica i servizi aziendali registrati a livello globale quando dovrebbero essere inclusi nella tua funzionalità.
- Cerca servizi in
libs/services/che appartengono chiaramente a un dominio specifico.
PUNTO 4 — Configurazione globale (app.config.ts o app.module.ts)
Trova il file di configurazione root di ciascuna app e analizza:
- Cosa è registrato a livello globale che non dovrebbe esserlo?
- I moduli funzionalità vengono caricati con caricamento lento o vengono importati direttamente?
- Sono presenti servizi aziendali nella configurazione globale?
- Quanti fornitori sono registrati in totale a livello globale?
FORMATO DEL RAPPORTO
Per ogni punto usa questa struttura esatta:
✅ Cosa c'è di buono
(elenco concreto con esempi di codice reale)
⚠️Segnaletica da controllare
(elenco concreto con il percorso del file e perché è un segno)
❌ Problemi chiari
(elenco concreto con percorso del file, problema e impatto effettivo)
Alla fine include:
Sintesi
Una tabella con i 4 punti, un emoji di stato (✅ ⚠️ ❌) e una linea di conclusione per punto.
Passaggi successivi prioritari
Elenco di massimo 5 azioni specifiche ordinate per impatto, con il percorso esatto del file o della cartella da toccare.
Sii diretto. Don't explain what a feature or theory is. Basta analizzare questo progetto specifico e fornire risultati reali.
