
Stiamo entrando in un’era in cui i sistemi frontend non sono utilizzati solo da esseri umani: vengono anche interpretati, generati e analizzati da strumenti basati sull’intelligenza artificiale.
Framework moderni come shadcn/ui, strumenti design-to-code come V0.dev e agenti IDE come Junie stanno ridefinendo il modo in cui costruiamo interfacce. Ma questa accelerazione richiede maggiore struttura.
Questa guida illustra un’architettura pratica per:
- Migliorare la creazione di UI con strumenti AI
- Progettare protocolli di collaborazione tra developer e AI
- Mantenere testabilità e scalabilità
1. Definisci linee guida AI per il tuo codice
La maggior parte degli strumenti AI non ha intuizione, quindi… forniscigliela. Crea un file guidelines.md
che descriva le regole, lo stack e i pattern usati nel tuo frontend.
Esempio:
- Framework: Next.js
- Stile: Tailwind CSS 4
- Linguaggio: TypeScript
- Test: Vitest + Playwright
Architettura:
- Pattern Use Case
- Middleware chaining
Standard:
- Solo export nominati
- Usare union types, evitare enum
- FC con PropsWithChildren
Agenti IDE come Junie useranno queste indicazioni per seguire le tue convenzioni senza doverle ripetere ogni volta.
Puoi vedere un esempio reale qui.
2. UI guidata dai componenti con supporto AI
Con strumenti come V0.dev, puoi passare da un prompt al JSX in pochi secondi:
“Crea un form di iscrizione in dark mode con due campi input e un pulsante CTA”
V0 risponde con JSX accessibile e già stilizzato usando shadcn/ui, pronto da inserire nel tuo repository. Combinato con Storybook, documentazione MDX e linee guida di Junie, ottieni un ciclo di feedback tra AI e il tuo design system.
L’AI genera. Il tuo sistema valida.
3. Il pattern Use Case per la logica di business
Nel development aumentato dall’AI, generare UI è solo una parte. Il vero valore è nel separare la logica di business dagli strati di interfaccia, in modo che agenti AI (o umani) possano attivare comportamenti in modo sicuro e prevedibile.
Ecco dove il Use Case pattern diventa essenziale.
Cos’è il Use Case Pattern?
Ogni use case incapsula un’operazione di business.
Esempi: RegisterUser
, CreatePost
, SubmitFeedback
.
tsCopiaModificaexport interface UseCase<In = unknown, Out = unknown> {
handle(param?: In, meta?: UseCaseOptions): Promise<Out>
}
Ogni use case è isolato, testabile e iniettabile. È completamente indipendente dal framework UI o dalla sorgente di eventi, quindi può essere attivato da:
- Un pulsante nella UI
- Un job in background
- Un agente AI
- Un webhook
- O una combinazione di questi
Perché è importante per l’AI?
Offrendo un’interfaccia stabile e nascondendo la logica interna, il pattern permette agli agenti AI di orchestrare comportamenti dell’applicazione senza conoscere i dettagli implementativi.
Previene l’accoppiamento tra logica e componenti, rendendo il codice più robusto e manutenibile.
Esecuzione centralizzata con useCaseService
Per eseguire gli use case si usa un service layer che astrae la logica di orchestrazione e consente la composizione tramite middleware:
tsCopiaModificaawait useCaseService.execute(RegisterUserUseCase, {
email: "test@example.com",
password: "secure-password",
})
L’AI non vede la logica interna. Sa solo che deve passare un payload e otterrà un risultato.
Vantaggi nei sistemi con AI:
- Testabilità: ogni use case può essere testato in isolamento
- Sicurezza: controlli di accesso via middleware
- Estendibilità: aggiunta di comportamenti (es. analytics) senza toccare la logica core
- Pronto per l’AI: gli agenti possono interagire in modo sicuro con un’API chiara e stabile
4. Middleware chaining: scalare la logica con composabilità
In un’architettura frontend ben strutturata, le concern trasversali (logging, errori, caching) possono complicare la logica di business se non gestite correttamente.
Il middleware offre un approccio modulare e pulito, ispirato al pattern Chain of Responsibility.
Invece di duplicare la logica, la incapsuli in classi middleware che intercettano l’esecuzione:
tsCopiaModificainterface Middleware {
intercept<In, Out>(
param: In,
next: UseCase<In, Out>,
options: UseCaseOptions
): Promise<Out>
}
Ogni middleware gestisce una singola responsabilità e passa il controllo al successivo.
Middleware degli errori
Niente più try/catch
duplicati. Un middleware può gestire gli errori e notificare l’interfaccia utente (es. toast, log) senza toccare la logica principale.
tsCopiaModificaexport class ErrorMiddleware implements Middleware {
constructor(private readonly eventEmitter: EventEmitter) {}
async intercept(params: unknown, next: UseCase, options: UseCaseOptions): Promise<unknown> {
try {
return await next.handle(params)
} catch (error) {
if (!options.silentError) {
this.eventEmitter.dispatch(EventType.ERROR, error)
}
throw error
}
}
}
Middleware di logging
Log automatici di ogni use case con parametri e nome. Supporta logger personalizzati per dev e produzione.
tsCopiaModificaexport class LogMiddleware implements Middleware {
constructor(private readonly logger: Logger) {}
intercept(params: unknown, useCase: UseCase): Promise<unknown> {
this.logger.log(
`[${DateTime.fromNow().toISO()}] ${this.getName(useCase)} / ${this.printResult(params)}`
)
return useCase.handle(params)
}
private getName(useCase: UseCase): string {
if (useCase instanceof UseCaseHandler) {
return this.getName(useCase.useCase)
}
return useCase.constructor.name
}
private printResult(result: unknown) {
return JSON.stringify(result, null, 2)
}
}
Middleware di caching
Se usi CQRS, puoi usare un middleware che gestisce automaticamente la cache per i use case che forniscono una cacheKey
.
tsCopiaModificaexport class CacheMiddleware implements Middleware {
private readonly store = new Map<string, CacheEntry>()
constructor(private readonly ttlInSeconds: number = 60) {}
async intercept<In, Out>(
params: In,
next: UseCase<In, Out>,
options: UseCaseOptions,
): Promise<Out> {
const key = options.cacheKey
if (!key) return next.handle(params, options)
const now = Date.now()
const cached = this.store.get(key)
if (cached && now < cached.expiresAt) {
return cached.value as Out
}
const result = await next.handle(params, options)
this.store.set(key, { value: result, expiresAt: now + this.ttlInSeconds * 1000 })
return result
}
}
Composizione dei middleware con UseCaseService
Per applicare più middleware, li incapsuli nel service che compone la catena da quello più esterno a quello più interno:
tsCopiaModificaexport class UseCaseService {
constructor(
private middlewares: Middleware[],
private readonly container: Container,
) {}
async execute<In, Out>(
useCase: Type<UseCase<In, Out>>,
param?: In,
options?: UseCaseOptions,
): Promise<Out> {
const requiredOptions = options ?? { silentError: false }
let next = UseCaseHandler.create({
next: this.container.create(useCase),
middleware: this.container.get<EmptyMiddleware>(EmptyMiddleware.name),
options: requiredOptions,
})
for (let i = this.middlewares.length - 1; i >= 0; i--) {
const current = this.middlewares[i]
next = UseCaseHandler.create({
next,
middleware: current,
options: requiredOptions,
})
}
return next.handle(param) as Promise<Out>
}
}
Perché è importante
Con i middleware in funzione, il tuo sistema ottiene:
- Gestione centralizzata degli errori
- Logging coerente
- Caching automatico
- Use case più puliti, senza duplicazioni
Scali la funzionalità senza aumentare la complessità della logica core.
Tabella riepilogativa: architettura aumentata dall’AI
Livello | Strumento / Pattern | Ruolo dell’AI |
---|---|---|
UI Prototyping | V0.dev + shadcn/ui | Generazione di JSX da prompt |
Documentazione UI | MDX + Storybook | Apprendimento da esempi |
Logica | Pattern Use Case | Creazione e invocazione di logica |
Integrazione | Catena di Middleware | Potenzia senza accoppiare |
IDE | .junie/guidelines.md | Allinea l’AI alle tue convenzioni |
Considerazioni finali
Non stiamo più costruendo solo per browser—costruiamo sistemi comprensibili sia per esseri umani che per agenti intelligenti.
Il futuro del frontend non è automatizzato. È aumentato.
Progetta la tua architettura pensando a:
- Interpretabilità (per persone e macchine)
- Struttura (tramite convenzioni e pattern)
- Scalabilità (con use case e middleware)
Questo è il modo in cui sviluppiamo con l’AI, non nonostante l’AI.
Vuoi aiuto per implementare questa architettura nel tuo team? Offro consulenza su architettura frontend e flussi di lavoro potenziati dall’AI. Contattami o scopri di più su cesalberca.com.