Ho un livello di dominio, che la mia architettura di applicazione contiene "sacro"; vale a dire, il livello del dominio non ha riferimenti né all'archiviazione dei dati, né ai problemi di presentazione (viceversa è consentito). Mi piacerebbe tenerlo così !
Tutto stava andando abbastanza bene finché non sono arrivato a un particolare requisito. Dato:
public class A
{
public string IdA { get; private set; }
public void DoWork()
}
public class B
{
public string IdB { get; private set; }
public string IdA { get; private set; }
public Invaidate()
}
In alcune condizioni insolite, che A
decide, quando DoWork
è chiamato tutto B
associato deve essere invalidato. Il problema, questa invalidazione richiede persistenza. Nota: questo caso limite è l'unica volta in cui A
dovrebbe riguardare il suo B
s associato. Il requisito per questa invalidazione non sarebbe ovvio per un consumatore di A
.
Ho ideato e implementato una soluzione (1), oltre a pensare ad altre soluzioni. Tuttavia, non ne sono abbastanza soddisfatto, o degli altri, e desidero un input.
Soluzione 1: A
Invalidates B
s, chiamate Delegate per la persistenza
Vantaggio: trasparente per il consumatore, facile da consumare
Svantaggio: comportamento di salvataggio strano e incoerente, nessun'altra classe nel livello di dominio salva quando viene chiamato un metodo
class A
{
private Action<B> SaveB; // Populated by ARepository when instance is retrieved, delegate points to sister repo BRepository
public List<B> Bees { get; private set; }
public DoWork()
{
// if edgeCase:
foreach (B b in Bees)
{
b.Invalidate();
SaveB(b);
}
}
}
Soluzione 2: A
Invalidates B
s, A
Repository Salva associato B
s
Vantaggio: trasparente al consumatore, facile da consumare, coerente livello di dominio
Svantaggio: strato persistente incoerente, antipattern di persistenza monolitico *
* Forse il nome anti-pattern sbagliato?
class A
{
public List<B> Bees { get; private set; }
public DoWork()
{
// if edgeCase:
foreach (B b in Bees)
b.Invalidate();
}
}
//...
class ARepository
{
Update(A a)
{
Sql.SaveA(a);
bRepository.Update(a.Bees);
}
}
Soluzione 3: evento Trigger On Edge Case
Vantaggio: nessuna incongruenza nel comportamento del dominio o del livello di persistenza, nessuna preoccupazione per le entità incrociate
Svantaggio: difficile da consumare, espone la logica al consumatore, che dovrebbe essere responsabile del cablaggio dell'evento?!
class A
{
public Event SaveBs/EdgeCaseHappened;
public List<B> Bees { get; private set; }
public DoWork()
{
// if edgeCase:
foreach (B b in Bees)
b.Invalidate();
SaveBs.Fire();
// or just...
EdgeCaseHappened.Fire()
}
}
Mentre ho già implementato 1, nessuna di queste tre soluzioni mi sembra del tutto giusta, c'è qualcosa qui che mi manca? Ho fatto un'assunzione errata da qualche parte?
Qual è il modo migliore di agire anche per me per mantenere le preoccupazioni trasversali come questa mantenibili e utilizzabili, isolando rigorosamente la mia logica aziendale?
Nota: questo è non un progetto DDD