Stiamo completamente rimodellando un sistema presso la società per la quale sto attualmente lavorando. Stiamo applicando il DDD e per la prima volta ho effettivamente qualcuno nel mio team che ha anche qualche precedente esperienza con DDD (yay!)
Questo nuovo sistema è estremamente incentrato sull'utente, cioè quasi tutte le operazioni all'interno del sistema provengono dall'utente finale. Alcune di queste operazioni potrebbero essere:
- Crea un account . Un utente non pagante è limitato a un account attivo, i membri pro possono avere tutti gli account che desiderano.
- Aggiungi una Transazione a un account , ma solo se un utente ha accesso a detto Account .
- Riordina Divisione s in un account , ma solo se un utente ha accesso a detto Account .
Ci sono molte di queste regole, tutte puntano a un singolo utente e questo è il punto in cui io e il mio collega siamo entrati in discussione.
La mia opinione è che gli aggregati dovrebbero essere tanto piccoli quanto necessari, quanto coesivi, questo rende più facile la loro verifica e comprensione. Per esempio. se modellino la prima regola, creerei un semplice aggregato, che potrebbe apparire come questo:
class UserWithActiveAccounts {
private UserId id;
private NonNegativeNumber countOfActiveAccounts;
private MembershipType membershipType;
public Account createNewAccount(AccountId accountId, NonEmptyString accountName) {
if (
MembershipType.FREE.equals(membershipType) &&
countOfActiveAccounts.GT(NonNegativeNumber.fromValue(1))
) {
throw AccountLimitExceeded.forFreeUserTooManyAccounts(id);
}
return Account.withOwner(accountId, accountName, id);
}
}
Questo aggregato contiene solo il conteggio degli account attualmente attivi e il tipo di iscrizione dell'utente, perché è questo che interessa la regola aziendale quando si crea un nuovo account.
Quindi, se dovessi modellare gli altri 2 casi utente, avrei bisogno di un altro aggregato, come UserWithAccessibleAccounts
, che contenga internamente un List
di AccountId
valori, che potrei ripetere sull'aggiunta di un < strong> Transazione con uno specifico AccountId
da controllare, indipendentemente dal fatto che l'operazione sia consentita o meno.
class UserWithAccessibleAccounts {
private UserId id;
private List<AccountId> accessibleActiveAccounts;
public Transaction addTransaction(
TransactionId transactionId,
AccountId toAccountId,
Number value
) {
if (hasAccessToAccount(toAccountId)) {
throw CannotAddTransaction.noAccessToAccount(id, toAccountId);
}
return Transaction.byUserInAccount(transactionId, value, id, toAccountId);
}
private bool hasAccessToAccount(AccountId accountId) {
return accessibleActiveAccounts.contains(accountId);
}
}
Ovviamente, con il mio approccio avrai un sacco di piccoli aggregati ciascuno responsabile di una piccola regola aziendale.
Ciò che il mio collega vorrebbe è avere una classe grande, come User
, che conterrebbe tutte le operazioni. Che sono contro Credo che porterebbe ad una classe disordinata, dove sarebbe molto più difficile rintracciare i bug. Inoltre, con il crescere di questa classe, ogni volta che si desidera eseguire una qualsiasi delle operazioni sull'utente, è necessario caricare molte proprietà che non sono correlate all'operazione che l'utente sta tentando di eseguire (come il caricamento i valori di List
di AccountId
quando è necessario un semplice conteggio di essi, in questo esempio molto semplificato).
Stiamo essenzialmente cercando di risolvere il problema di:
- o doversi inventare un sacco di nomi di classe per tutti quei piccoli aggregati,
- affronta il problema della scalabilità e una classe gigantesca grazie al prelievo di tutti i dati per ciascuna operazione, indipendentemente dalla loro entità.
Sono propenso all'approccio dei piccoli aggregati, ma forse non lo guardo dall'angolo corretto e c'è qualcosa nell'approccio che il mio collega sta suggerendo che semplicemente non sto vedendo.