Vorrei iniziare scusandomi prima per la lunghezza del post, ma volevo davvero trasmettere tutti i dettagli in anticipo, quindi non mi prendo il tuo tempo andando avanti e indietro nei commenti.
Sto progettando un'applicazione seguendo un approccio DDD e mi chiedo quali linee guida posso seguire per determinare se una radice aggregata dovrebbe contenere un altro AR o se debbano essere lasciati come AR separati "indipendenti".
Prendiamo il caso di una semplice applicazione Time Clock che consente ai Dipendenti di autenticarsi o meno per il giorno. L'interfaccia utente consente loro di inserire l'ID e il PIN dell'impiegato, che viene quindi convalidato e recuperato lo stato corrente dell'impiegato. Se il dipendente è attualmente connesso, l'interfaccia utente visualizza un pulsante "Clock Out"; e, al contrario, se non sono sincronizzati, il pulsante indica "Clock In". L'azione intrapresa dal pulsante corrisponde anche allo stato dell'impiegato.
L'applicazione è un client Web che chiama un server back-end esposto tramite un'interfaccia di servizio RESTful. Il mio primo passaggio alla creazione di URL intuitivi e leggibili ha portato ai seguenti due endpoint:
http://myhost/employees/{id}/clockin
http://myhost/employees/{id}/clockout
NOTA: vengono utilizzati dopo la convalida dell'ID e del PIN dell'impiegato e il passaggio di un "token" che rappresenta "utente" in un'intestazione. Questo perché esiste una "modalità manager" che consente a un manager o supervisore di accedere o disconnettere un altro dipendente. Ma, per il bene di questa discussione, sto cercando di renderlo semplice.
Sul server, ho un ApplicationService che fornisce l'API. La mia idea iniziale per il metodo ClockIn è qualcosa del tipo:
public void ClockIn(String id)
{
var employee = EmployeeRepository.FindById(id);
if (employee == null) throw SomeException();
employee.ClockIn();
EmployeeRepository.Save();
}
Questo sembra piuttosto semplice fino a quando non ci rendiamo conto che le informazioni sulla time card del Dipendente vengono effettivamente mantenute come un elenco di transazioni. Ciò significa che ogni volta che chiamo ClockIn o ClockOut, non cambio direttamente lo stato del Dipendente ma, invece, aggiungo una nuova voce nel TimeSheet del Dipendente. Lo stato corrente del Dipendente (con o senza clock) deriva dalla voce più recente nel TimeSheet.
Quindi, se vado con il codice mostrato sopra, il mio repository deve riconoscere che le proprietà permanenti del Dipendente non sono cambiate ma che è stata aggiunta una nuova voce nel TimeSheet del Dipendente ed eseguire un inserimento nell'archivio dati.
D'altra parte (ed ecco l'ultima domanda del post), TimeSheet sembra che sia una radice aggregata oltre che abbia identità (ID impiegato e periodo) e potrei implementare altrettanto facilmente la stessa logica di TimeSheet.ClockIn (ID Dipendente).
Mi trovo a discutere i meriti dei due approcci e, come affermato nel paragrafo iniziale, mi chiedo quali criteri dovrei valutare per determinare quale approccio è più adatto al problema.