Situazione
All'inizio di questa sera ho dato una risposta a una domanda su StackOverflow.
La domanda:
La modifica di un oggetto esistente dovrebbe essere eseguita a livello di repository o in servizio?
Ad esempio se ho un utente con debito. Voglio cambiare il suo debito. Devo farlo in UserRepository o in servizio, ad esempio BuyingService, ottenendo un oggetto, modificandolo e salvandolo?
La mia risposta:
Dovresti lasciare la responsabilità di mutare un oggetto sullo stesso oggetto e utilizzare il repository per recuperare questo oggetto.
Esempio di situazione:
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
Un commento che ho ricevuto:
La logica aziendale dovrebbe davvero essere in un servizio. Non in un modello.
Cosa dice internet?
Quindi, questo mi ha fatto cercare poiché non ho mai usato (consapevolmente) un livello di servizio. Ho iniziato a leggere sul modello del livello di servizio e sul modello unità di lavoro, ma finora non posso dire di essere convinto che debba essere utilizzato un livello di servizio.
Prendiamo ad esempio questo articolo di Martin Fowler sull'anti-schema di un modello di dominio anemico:
Esistono oggetti, molti dei quali prendono il nome dai nomi nello spazio del dominio e questi oggetti sono collegati con le ricche relazioni e la struttura che hanno i veri modelli di dominio. La cattura viene quando si osserva il comportamento e ci si rende conto che non c'è quasi alcun comportamento su questi oggetti, rendendoli poco più che sacchi di getter e setter. In effetti, spesso questi modelli sono dotati di regole di progettazione che dicono che non è necessario inserire alcuna logica di dominio negli oggetti dominio. Esistono invece una serie di oggetti di servizio che acquisiscono tutta la logica del dominio. Questi servizi vivono in cima al modello di dominio e usano il modello di dominio per i dati.
(...) La logica che dovrebbe trovarsi in un oggetto dominio è la logica del dominio - convalide, calcoli, regole aziendali - come preferisci chiamarlo.
Per me, questo sembrava esattamente ciò che la situazione riguardava: ho sostenuto la manipolazione dei dati di un oggetto introducendo metodi all'interno di quella classe che fanno proprio questo. Tuttavia mi rendo conto che questo dovrebbe essere un dato modo e probabilmente ha più a che fare con il modo in cui questi metodi vengono invocati (usando un repository).
Ho anche avuto la sensazione che in quell'articolo (vedi sotto), un livello di servizio sia più considerato come una facciata che delega il lavoro al modello sottostante, piuttosto che un livello ad alta intensità di lavoro.
Livello applicazione [il suo nome per livello di servizio]: definisce i lavori che il software dovrebbe svolgere e indirizza gli oggetti del dominio espressivo a risolvere i problemi. Le attività di cui questo livello è responsabile sono significative per l'azienda o necessarie per l'interazione con i livelli di applicazione di altri sistemi. Questo strato è mantenuto sottile. Non contiene regole o conoscenze aziendali, ma coordina solo attività e delegati lavorano a collaborazioni di oggetti di dominio nel livello successivo in basso. Non ha uno stato che riflette la situazione aziendale, ma può avere uno stato che riflette l'avanzamento di un'attività per l'utente o il programma.
Che è rafforzato qui :
Interfacce di servizio. I servizi espongono un'interfaccia di servizio a cui vengono inviati tutti i messaggi in entrata. Puoi pensare a un'interfaccia di servizio come una facciata che espone la logica di business implementata nell'applicazione (in genere, la logica nel livello aziendale) ai potenziali consumatori.
E qui :
Il livello di servizio dovrebbe essere privo di qualsiasi applicazione o logica aziendale e dovrebbe concentrarsi principalmente su alcune preoccupazioni. Dovrebbe racchiudere le chiamate del livello aziendale, tradurre il dominio in una lingua comune che i clienti possano comprendere e gestire il mezzo di comunicazione tra il server e il client richiedente.
Questo è un grave contrasto con altre risorse che parlano del livello di servizio:
Il livello di servizio deve essere costituito da classi con metodi che sono unità di lavoro con azioni che appartengono alla stessa transazione.
O la seconda risposta a una domanda che ho già collegato:
Ad un certo punto, l'applicazione richiederà qualche logica aziendale. Inoltre, potresti voler convalidare l'input per assicurarti che non ci sia qualcosa di malvagio o non performante richiesto. Questa logica appartiene al tuo livello di servizio.
"Soluzione"?
Seguendo le linee guida in questa risposta , ho trovato il seguente approccio che utilizza un livello di servizio:
class UserController : Controller {
private UserService _userService;
public UserController(UserService userService){
_userService = userService;
}
public ActionResult MakeHimPay(string username, int amount) {
_userService.MakeHimPay(username, amount);
return RedirectToAction("ShowUserOverview");
}
public ActionResult ShowUserOverview() {
return View();
}
}
class UserService {
private IUserRepository _userRepository;
public UserService(IUserRepository userRepository) {
_userRepository = userRepository;
}
public void MakeHimPay(username, amount) {
_userRepository.GetUserByName(username).makePayment(amount);
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
Conclusione
Tutti insieme non è cambiato molto qui: il codice dal controller è passato al livello di servizio (il che è positivo, quindi c'è un lato positivo di questo approccio). Tuttavia, questo non sembra avere nulla a che fare con la mia risposta originale.
Mi rendo conto che i modelli di progettazione sono linee guida, non regole fissate nella pietra da attuare quando possibile. Tuttavia non ho trovato una spiegazione definitiva del livello di servizio e di come dovrebbe essere considerato.
È un mezzo per estrarre semplicemente la logica dal controller e inserirla in un servizio?
Dovrebbe formare un contratto tra il controller e il dominio?
Dovrebbe esserci un livello tra il dominio e il livello di servizio?
E, ultimo ma non meno importante: seguire il commento originale
La logica aziendale dovrebbe davvero essere in un servizio. Non in un modello.
È corretto?
- Come introdurrei la mia logica aziendale in un servizio anziché nel modello?