Sto sviluppando un sistema software, che fornisce API REST HTTP, e voglio ottenere un design molto modulare e flessibile. Quindi, c'è una funzionalità di base e moduli di funzionalità, che gestiscono solo la logica relativa a ciascuna caratteristica specifica. L'obiettivo è essere in grado di aggiungere / rimuovere moduli in modo flessibile senza la necessità di connettere manualmente ciascun modulo al core e ad altri moduli. Pertanto i moduli non dovrebbero avere dipendenze strette tra loro e il core dovrebbe essere molto piccolo e non dovrebbe dipendere da nessuna funzione.
Ho scoperto che l'architettura basata su eventi è un buon candidato per i miei obiettivi.
Tuttavia, ho alcune difficoltà a implementarlo in maniera robusta.
1. Come avvicinarsi alla condivisione delle informazioni?
Inizialmente, quando viene eseguita una logica all'interno dell'applicazione (ad esempio durante l'elaborazione della richiesta utente), esiste un contesto attorno ad essa. Ad esempio, alcune entità sono state caricate dal database (sto usando la libreria ORM per questo). E poi voglio lanciare un evento.
Quali dati dovrei passare con l'evento? Posso vedere due approcci:
- Passa alle entità correlate con l'evento
- Passa solo gli ID entità (cioè la chiave primaria, che potrebbe essere utilizzata per recuperare l'entità dal database)
Con il primo approccio, i dati potrebbero essere utilizzati subito dall'evento, questo è conveniente. Tuttavia, cosa succede se qualche evento precedente ha apportato alcune modifiche ai dati? Non possiamo saperlo con certezza, perché i gestori degli eventi sono disaccoppiati. Quindi i dati passati potrebbero essere già superati.
Con il secondo approccio stiamo forzando il caricamento di nuovi dati dal database, poiché inviamo solo ID e non dati reali. Quindi ogni gestore di eventi dovrà recuperare manualmente le entità. Questo risolve il problema della freschezza dei dati, ma porta ad un altro problema in termini di prestazioni. Potrebbero esserci dozzine di gestori di eventi che reagiscono a una singola richiesta dell'utente e ognuno invierà richieste al database. Questo sembra un approccio molto inefficace, che metterà l'accento sul database.
2. Come gestire la concorrenza (isolamento della transazione)?
Un altro problema è che dobbiamo assicurarci che il nostro codice sia al sicuro dal punto di vista della concorrenza. Non vogliamo che richieste parallele o anche parti diverse dello stesso processo modifichino gli stessi dati. Per fare ciò, dobbiamo utilizzare le transazioni del database e varie funzionalità di blocco.
E ancora, potremmo avviare una transazione e passarla con l'evento, così il gestore di eventi sarà in grado di utilizzare lo stesso livello di isolamento. O ogni gestore di eventi avrebbe la propria transazione. Ma questo porta di nuovo alla questione delle prestazioni del database.
Stavo pensando di creare una nuova transazione per ogni richiesta utente e di passarla a ciascun metodo, che richiede l'accesso al database (direttamente o con gli eventi). Pertanto ogni richiesta con tutti i possibili gestori di eventi verrà eseguita in un'unica transazione. È un buon approccio?
In generale, questa architettura solleva molte domande, sarei molto grato per qualsiasi buon libro o articolo su questo argomento. O forse c'è un approccio migliore per raggiungere i miei obiettivi in termini di flessibilità e modularità?