Skip to main content

Logiche di business

Services

I servizi sono collocati nel namespace \App\{module}\Services.

Ogni service è una classe istanziabile per consentire l'utilizzo del service container del framework e la dependency injection.

L'architettura dei service segue un approccio a più livelli di astrazione:

  • Service atomici: implementano logiche molto specifiche e circoscritte
  • Service intermedi: orchestrano più service atomici per implementare logiche di medio livello
  • Service di alto livello: coordinano service intermedi per implementare flussi complessi

Questo approccio garantisce:

  • Riutilizzabilità del codice in qualsiasi parte dell'applicazione
  • Facilità di testing automatico
  • Basso accoppiamento tra componenti
  • Chiara separazione delle responsabilità

I service accettano come input e restituiscono come output solo:

  • Tipi primitivi (int, string, bool)
  • Classi e DTO per strutture dati complesse

Un service può contenere più metodi correlati purché:

  • Siano utilizzati internamente dal service stesso
  • Mantengano coerenza con il modulo, l'entità e l'intento del service
    • ✅ PropertyAddressService con metodi per formattare indirizzi in modi diversi
    • ❌ ProjectService con metodi non correlati che condividono solo modulo ed entità

Per i service di alto livello è necessario:

  • ✅ Implementare la validazione degli input per garantire la coerenza dei dati
  • 🧪 Sviluppare unit test completi per verificarne il corretto funzionamento nel tempo

Controllers

I Controllers in questa architettura non eseguono direttamente logiche di business ma fungono da orchestratori, limitandosi a contenere esclusivamente le seguenti cose:

  • Vaildations (tramite i DTO usati come parametri delle action)
  • Authorizations (tramite il modulo di autorizzazione)
  • Services
  • Events

Nessuna logica di business dev'essere scritta direttamente nei controller.
I controller dovrebbero contenere le action necessarie a svolgere un unico flusso le cui azioni sono sequenziali o interdipendenti, ad eccezione per i controller di risorsa che custodiranno esclusivamente le azioni per il CRUD.

Comando per la creazione di un nuovo Controller

php artisan bb-make:controller {module} {controller}

Per esempio

php artisan bb-make:controller inv invoice

creerà

App/InvModule/Http/Controllers/InvInvoiceController.php

Events and Listeners

Gli eventi (Events) sono un meccanismo fondamentale per notificare in modo trasversale gli avvenimenti all'interno dell'applicazione.

Naming Conventions

Per gli eventi:

  • Il nome deve descrivere chiaramente l'evento avvenuto (non le conseguenze)
  • Deve seguire il pattern: {Modulo}{EventoAvvenuto}Event
  • ✅ Esempio corretto: McMetricCalculationStoredEvent
  • ❌ Esempio errato: GenerateSomethingEvent

Per i Listeners:

  • Il nome deve descrivere l'azione che eseguono
  • Deve seguire il pattern: {Modulo}{AzioneDaEseguire}Listener
  • Esempio: McGeneratePdfListener

Binding automatico degli Eventi

A partire da Laravel 12, il binding tra eventi e listeners può essere gestito automaticamente dal framework attraverso la type-hint dell'evento nel metodo handle del listener.

Non è più necessario registrare manualmente il binding nel service provider - è sufficiente dichiarare il tipo dell'evento come parametro del metodo handle:

<?php

namespace App\Shared\Listeners;

use App\Shared\Events\SharedFooEvent;

class SharedBarListener
{
public function __construct()
{
//
}

public function handle(SharedFooEvent $event): void
{
//
}
}

Best Practices

  • I Listeners devono essere il più possibile atomici
  • È preferibile associare più listeners ad un evento piuttosto che avere un listener monolitico
  • I Listeners, come i controllers, non devono contenere logica di business ma solo utilizzare i services

Quando Usare gli Eventi

Gli eventi sono indicati quando:

  • Servono operazioni collaterali non direttamente legate al flusso principale
  • È necessario disaccoppiare azioni secondarie dal flusso sincrono
  • Si vuole mantenere il codice modulare e facilmente estendibile

Comando per la creazione di nuovi Event e Listeners

php artisan bb-make:event {module} {event}
php artisan bb-make:listener {module} {listener}

Per esempio

php artisan bb-make:event cont contractSigned

creerà

App/ContModule/Events/ContContractSignedEvent.php

e

php artisan bb-make:listener cont notifyCustomer

creerà

App/ContModule/Listeners/ContNotifyCustomerListener.php

Jobs

I Jobs sono componenti fondamentali per la gestione di operazioni asincrone e dovrebbero essere utilizzati principalmente all'interno dei Listeners.
Questo approccio mantiene gli eventi come sistema principale per la gestione delle operazioni asincrone, offrendo la flessibilità di aggiungere nuovi Jobs a un evento senza modificare il controller originale.

Per i Jobs richiamati direttamente da comandi CLI, non è necessario implementare il pattern Event/Listener.

Come per i controllers, un Job non deve contenere logica di business ma deve limitarsi a orchestrare i services necessari per completare l'operazione.

Quando utilizzare un Job?

  • Per operazioni che richiedono tempi di elaborazione significativi
  • Per task che possono essere eseguiti in modo asincrono rispetto al flusso principale
  • Per operazioni complesse con possibilità di fallimento, dove è necessario implementare meccanismi di retry
  • Quando è importante monitorare l'esecuzione dell'operazione (attraverso Laravel Horizon)

Decision Tree: Service vs Event/Listener vs Job

📌 1. È un'operazione non estraibile dal flusso del controller?

├── ✅ Sì → Service nel Controller

└── ❌ No


📌 2. È un'operazione veloce con risultato certo?

├── ✅ Sì → Event + Listener
│ (Service nel Listener)

└── ❌ No


📌 3. È un'operazione lunga o soggetta a errori?

└── ✅ Sì → Event + Listener + Job
(Service nel Job)