In un approccio DDD, supponiamo di avere un Order
(questa è la mia radice aggregata) e un Line
(un discendente di Order
). In entrambi gli oggetti ho un setter privato per le proprietà e un builder che gestisce le creazioni del cluster di oggetto.
Per quanto ne so, ogni azione sul cluster passa attraverso la radice aggregata. È giusto?
Ad esempio, se ho bisogno di un comando per nascondere una riga non posso avere semplicemente un metodo hide()
sull'oggetto Line
, ma invece dovrei avere un metodo hideLine()
nell'aggregato root che attraversa il cluster di oggetti fino a Line
.
A questo punto immagino uno scenario del genere:
// in the aggregate root
public void HideLine(Line line){
line.Hide();
}
// in the Line class
public void Hide(){
this.Hidden = true;
}
ma in questo modo qualcuno potrebbe fare qualcosa di simile:
Order orderA = _repo.getById(id_A);
Order orderB = _repo.GetById(id_B);
orderB.HideLine(orderA.Lines.First());
con effetti collaterali sconosciuti (in questo momento l'effetto collaterale è chiaro, ma in uno scenario più complesso questo potrebbe creare mal di testa).
Quindi potrei prendere queste contromisure:
- Accetta un id come parametro per
HideLine
mehtd (cioè pubblico pubblicoHideLine(Guid lineId)
) - Verifica in
HideLine(Line line)
metodo cheline
è effettivamente inOrder.Lines
raccolta
Come gestisco i problemi di coerenza? Le mie soluzioni sono significative o mi manca il punto?
Post scriptum dopo i commenti
@Songo: l'aggregato Order
(nota: nel mio scenario reale non è un Ordine ma un cluster più complesso) avvia un gestore di saga / processo durante la sua vita che sovrintende al processo di pubblicazione Order
(prova a immagina che in uno scenario reale sia necessario pubblicare un ordine). Appena viene catturato OrderPublishingRequestEvent
, inizia la saga e prova a convalidare il Order
controllando se ogni Line
è valido, e in caso contrario la linea verrà nascosta.
Quindi il mio gestore di comandi è qualcosa del tipo:
public void Handle(OrderValidationCommand message)
{
// get the aggregate
Order order = _repository.GetById(message.OrderId);
// call a service to make validation
foreach (Line line in order.Lines)
{
var result = validator.Validate(line);
if (result.IsValid)
order.HideLine(line);
else
// do something else
}
order.Validate(); // raise event ValidatedOrderEvent
_repository.Update(fornitura);
}