Architettura
Definizione dell'architettura
La piattaforma è strutturata come un'applicazione monolitica Laravel che incorpora al suo interno diverse sotto-applicazioni chiamate moduli. I moduli sono distribuiti in diverse directory dell'applicazione:
app/: contiene le logiche di business dei modulidatabase/: contiene migrations, factories e seeders dei moduliroutes/: contiene le definizioni delle rotte dei modulistorage/app/private/: contiene i file privati dei modulistorage/logs/: contiene i logs dei singoi modulitests/: contiene feature e unit tests dei moduli
Il modulo shared è architetturalmente identico agli altri moduli, ma ha un ruolo particolare: fornisce definizioni, servizi e componenti frontend che vengono utilizzati trasversalmente da tutti gli altri moduli dell'applicazione.
Struttura della piattaforma
📁 agora-platform
├── 📁 app
│ ├── 📁 Module
│ │ ├── 📁 Console
│ │ ├── 📁 Dtos
│ │ ├── 📁 Http
│ │ │ ├── 📁 Controllers
│ │ │ ├── 📁 Middleware
│ │ ├── 📁 Mappers
│ │ ├── 📁 Models
│ │ ├── 📁 Providers
│ │ └── 📁 Services
│ └── 📁 Shared
│ # same structure as module
├── 📁 bootstrap
├── 📁 config
├── 📁 database
│ ├── 📁 Module
│ │ ├── 📁 factories
│ │ ├── 📁 migrations
│ │ └── 📁 seeders
│ └── 📁 Shared
│ # same structure as module
├── 📁 public
├── 📁 routes
│ ├── 📁 module
│ │ ├── 📄 api.php
│ │ ├── 📄 channels.php
│ │ ├── 📄 console.php
│ │ └── 📄 web.php
│ ├── 📄 api.php
│ ├── 📄 channels.php
│ ├── 📄 console.php
│ └── 📄 web.php
├── 📁 storage
│ ├── 📁 app
│ │ ├── 📁 private
│ │ │ ├── 📁 Module
│ │ │ └── 📁 Shared
│ │ └── 📁 public
│ ├── 📁 framework
│ └── 📁 logs
│ ├── 📁 Module
│ └── 📁 Shared
├── 📁 tests
│ ├── 📁 Module
│ │ ├── 📁 Feature
│ │ └── 📁 Unit
│ └── 📁 Shared
│ ├── 📁 Feature
│ └── 📁 Unit
├── 📁 vendor
├── 📄 .env
├── 📄 .env.example
├── 📄 .gitignore
├── 📄 artisan
├── 📄 composer.json
├── 📄 package.json
└── 📄 phpunit.xml
Comandi artisan per la creazione di files
Per generare files come models, controllers o altro esiste una serie di comandi artisan specifici nel namespace bb-make.
Ecco la lista dei comandi disponibili:
bb-make:migration: Crea una nuova migration per il databasebb-make:model: Crea un nuovo model con relativi traitsbb-make:dto: Crea un nuovo Data Transfer Objectbb-make:controller: Crea un nuovo controllerbb-make:job: Crea un nuovo job per operazioni in backgroundbb-make:event: Crea un nuovo eventbb-make:listener: Crea un nuovo listener per gestire eventi
Tutti i comandi sopra elencati, ad eccezione di migration, accettano due argomenti:
module: identifica il modulo in cui creare il file (es. "shared", "mc", "inv")name: il nome del file che verrà automaticamente:- prefissato con il prefisso del modulo
- suffissato con il tipo appropriato (Controller, Event, Listener, etc.) (eccetto i model)
Il name può includere sottocartelle usando il separatore "/", che verranno create automaticamente.
Per esempio php artisan bb-make:controller mc sub/folder/MetricCalculation creerà il file App/McModule/Http/Controllers/sub/folder/McMetricCalculationController.php.
Il comando migration funziona diversamente e oltre all'argomento module accetta due opzioni da usare singolarmente:
--name: funziona come ilnamedescritto sopra--table: per creare una migration di tabella (il nome viene generato automaticamente e il prefisso del modulo viene aggiunto)
Definizione dei Moduli
I moduli rappresentano aree funzionali distinte della piattaforma, ognuna dedicata a gestire specifici flussi di business, servizi e stati applicativi. Esempi di moduli includono wizard, computi metrici, contratti e altri componenti funzionali ben definiti.
Ogni modulo opera secondo il principio di responsabilità unica:
- Ha accesso in scrittura esclusivo alle proprie entità del database, attraverso utenti DB dedicati con permessi specifici
- Incapsula le logiche di business relative al proprio dominio
- Gestisce autonomamente le proprie interfacce utente e logiche di presentazione
Questa architettura modulare offre diversi vantaggi:
- Indipendenza: i moduli possono potenzialmente funzionare in modo autonomo
- Scalabilità: nuovi moduli possono essere aggiunti senza impattare quelli esistenti
- Manutenibilità: il codice è organizzato in unità logiche coese
- Collaborazione: team diversi possono lavorare su moduli separati in parallelo
Dal punto di vista tecnico, ogni modulo segue la struttura standard di una applicazione Laravel, comportandosi effettivamente come una "mini-applicazione" all'interno del sistema principale. Questo approccio garantisce familiarità per gli sviluppatori e consistenza nell'organizzazione del codice.
Architettura dei Moduli
L'applicazione è strutturata come un sistema modulare basato su API REST, dove ogni modulo possiede il proprio namespace specifico. I moduli sono suddivisi in due categorie principali:
Moduli Core
I moduli core rappresentano il cuore logico dell'applicazione, contenendo tutte le logiche relative a uno specifico prodotto. Ogni modulo core:
- Può possedere un proprio Project model con entità specifiche per gestire quel determinato tipo di progetto o gestisce progetti "esterni"
- Si occupa dell'avanzamento del progetto (gestione degli stati, task, workflow, ecc.)
- Conosce e gestisce i dati di business specifici del dominio di riferimento
Per esempio il modulo core "Agorà Casa" gestisce esclusivamente i progetti Agorà Casa e il loro ciclo di vita come task e gestione delle entità di progetto
Moduli Feature
I moduli feature sono progettati per essere verticali e isolati, fornendo servizi specializzati senza conoscere i dettagli dei progetti dei moduli core. Ogni modulo feature:
- Non ha conoscenza diretta dei progetti e delle entità dei moduli core
- Si occupa di funzionalità specifiche e specializzate
- Opera in modalità "standalone" o "contestualizzata"
Per esempio il modulo "contratti" gestisce la creazione e firma dei contratti, ma riceve i dati necessari dal modulo core chiamante o non riceve nulla se standalone
Flusso di Interazione
Modalità Standalone vs Contestualizzata
I moduli feature possono operare in due modalità distinte:
Modalità Standalone: L'utente accede direttamente al modulo feature, che richiede l'inserimento manuale di tutte le variabili necessarie per la sezione specifica.
Modalità Contestualizzata: L'utente naviga dal modulo core verso il modulo feature, che riceve un contesto precompilato con i dati necessari dal modulo core chiamante.
Esempio Pratico: Creazione Contratto di Appalto
Il seguente diagramma illustra il flusso completo per la creazione di un "Contratto di appalto" dal modulo core "Agorà Casa" verso il modulo feature "Contratti":
Dettaglio dei Passaggi
1. Autenticazione e Inizializzazione Contesto
L'utente si autentica nella piattaforma e viene automaticamente settato il contesto di base per il layout del modulo core "Agorà Casa". Questo contesto risiede nel database ed è strettamente associato all'utente autenticato.
2. Navigazione verso Creazione Contratto
L'utente, all'interno del contesto del modulo core Agorà Casa, naviga verso la funzionalità di creazione di un "Contratto di appalto". Il frontend Nuxt gestisce il routing e redirige l'utente verso il modulo dei contratti nella pagina di creazione.
3. Creazione Context Session nel core module
Il modulo core "Agorà Casa" espone una route di specifica per la creazione di una sessione di contesto tramite POST /api/ago/context-session. Quando viene chiamata questa API, il controller utilizza un servizio dedicato per:
- Recuperare tutti i dati necessari dal progetto corrente
- Creare una Context Session, un'entità figlia del contesto che rappresenta la sessione corrente dell'utente
- La Context Session contiene i dati specifici del progetto che serviranno al modulo feature
- Salvare la Context Session nel database associata all'utente
- Restituire il Context Session ID al frontend
Il contesto di base contiene solo le informazioni per contestualizzare l'app dal punto di vista di layout e UI, mentre la Context Session rappresenta una sessione temporanea con i dati specifici per l'operazione in corso.
4. Richiesta Template e Precompilazione
La pagina dei contratti richiama l'API del modulo feature "Contratti" per ottenere il template del "Contratto di appalto". Il modulo contratti:
- Verifica la presenza di una Context Session settata per l'utente
- Se la Context Session contiene dati, procede con la precompilazione automatica dei campi
- Restituisce il template già popolato con i dati della Context Session
5. Visualizzazione e Compilazione
L'utente visualizza il contratto richiesto con tutti i campi già precompilati con i dati provenienti dal modulo core. L'utente può modificare i campi precompilati e aggiungere le informazioni mancanti prima di confermare la creazione del contratto.
6. Creazione Effettiva Contratto
Una volta confermato dal frontend, viene effettuata una chiamata POST /api/cont/contracts al modulo feature "Contratti" che:
- Salva il contratto nel database del modulo contratti
- Emette un evento
ContractCreatedche include il Context Session ID nella sua firma - Restituisce conferma di successo al frontend
Alcuni servizi come quello di creazione di un contratto emettono eventi alla fine del processo. Nel caso dei contratti, l'evento viene sempre chiamato e include il Context Session ID per permettere al modulo core di tracciare l'operazione.
7. Gestione Evento nel Core Module
Il modulo core "Agorà Casa" ha un listener registrato per l'evento ContractCreated. Quando l'evento viene ricevuto:
- Il listener utilizza il Context Session ID per risalire al project_id originale
- Associa il contratto appena creato al progetto corrispondente
- Esegue il cleanup della Context Session, eliminando i dati temporanei
- Aggiorna lo stato del progetto se necessario
Pattern di Integrazione
Questo pattern di integrazione tra moduli core e feature è standardizzato e si applica a tutti i flussi che coinvolgono il passaggio da un modulo core a un modulo feature:
- Setup della Context Session: Il modulo core crea una Context Session temporanea con i dati specifici per l'operazione
- Gateway API: Il modulo core espone endpoint gateway POST che gestiscono la creazione della Context Session
- Verifica Context Session: Il modulo feature verifica sempre la presenza di una Context Session prima di procedere
- Precompilazione: Se presente, la Context Session viene utilizzata per precompilare automaticamente i dati
- Creazione Entità: Il modulo feature crea l'entità richiesta ed emette un evento con il Context Session ID
- Gestione Evento: Il modulo core riceve l'evento tramite listener e associa l'entità creata al progetto originale
- Cleanup: Il modulo core esegue il cleanup della Context Session dopo aver completato l'operazione
- Fallback: In assenza di Context Session, il modulo feature opera in modalità standalone
Questo approccio garantisce una separazione netta delle responsabilità, mantenendo i moduli feature agnostici rispetto ai dettagli implementativi dei moduli core, mentre permette una user experience fluida e contestualizzata e un tracciamento completo delle operazioni cross-modulo.