Sto sviluppando la mia prima applicazione utilizzando DDD e event sourcing.
Da quanto ho capito, l'aggregazione incapsula funzionalità di dominio specifiche con uno o più invarianti associati. Gli invarianti sono tradotti in vincoli di coerenza: in qualsiasi momento lo stato dell'aggregato deve essere coerente (gli invarianti sono conservati).
Fin qui tutto bene.
La maggior parte delle esercitazioni che ho visto suggeriscono che le radici aggregate dovrebbero essere ottenute in questo modo:
UserAggregate user = mUsersRepository.getUser(userId);
A questo punto mi sento confuso.
Supponiamo che ogni chiamata a UsersRepository
restituisca una nuova istanza di UserAggregate
("approccio 1"). Questo può essere utile perché elimina la necessità di garantire la sicurezza del thread (nel codice). Tuttavia, se più oggetti UserAggregate
verranno utilizzati contemporaneamente, questi oggetti potrebbero non essere sincronizzati tra loro (pur rimanendo coerenti singolarmente). Ho la sensazione che avere diverse istanze "non sincronizzate" dello stesso aggregato possa portare a tutti i tipi di bug sgradevoli, ma non posso davvero esserne certo.
Ora supponiamo che ogni chiamata a UsersRepository
restituisca lo stesso oggetto UserAggregate
("approccio 2"). Quindi l'istanziazione e la memorizzazione nella cache di UserAggregate
all'interno di UserRepository
devono essere rese thread-safe. Inoltre, la memorizzazione nella cache deve basarsi su riferimenti deboli; in caso contrario, l'intero database degli utenti verrà tenuto in memoria. La mia preoccupazione principale, tuttavia, è che in questo scenario UserAggregate
stesso debba diventare thread-safe. Sembra che sarà un importante PITA per garantire la sicurezza del thread di tutte le radici aggregate. Può anche diventare un problema di prestazioni. Non menzionare il rischio associato a bug multi-threading ...
Se fosse tutto, probabilmente andrei con "approccio 2" perché, almeno, so come renderlo sicuro. Tuttavia, esiste questa nozione di blocco ottimistico basato sulla versione aggregata che (a mio modo di vedere) si applica a "approccio 1".
Se ho capito bene, a ogni aggregato viene associata una versione. Ad ogni aggiornamento dello stato di aggregazione viene verificata la versione e, se la versione in DB non è uguale alla versione dell'aggregazione memorizzata nella cache, l'aggiornamento non riesce. Quindi lo stato aggregato può essere sincronizzato dal database e l'aggiornamento riprovato. Questo sembra indirizzare le mie paure legate all'incongruenza tra le diverse istanze dell'aggregato. Tuttavia, sembra molto lavoro - ogni metodo che aggiorna l'aggregato può fallire, quindi il codice che lo utilizza deve essere in grado di riprovare l'operazione.
Le mie domande sono:
- La mia comprensione (riassunta sopra) è corretta?
- Ci sono altri problemi associati all'approccio 1/2 che non vedo?
- Ci sono altri approcci che non vedo?
- Esiste un approccio standard utilizzato dalla comunità DDD?