Situación
A principios de esta tarde, respondí a una pregunta sobre StackOverflow.
La pregunta:
¿La edición de un objeto existente debe hacerse en la capa de repositorio o en servicio?
Por ejemplo, si tengo un usuario que tiene deudas. Quiero cambiar su deuda. ¿Debo hacerlo en UserRepository o en el servicio, por ejemplo, BuyService obteniendo un objeto, editándolo y guardándolo?
Mi respuesta:
Debe dejar la responsabilidad de mutar un objeto a ese mismo objeto y usar el repositorio para recuperar este objeto.
Situación de ejemplo:
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 comentario que recibí:
La lógica empresarial realmente debería estar en un servicio. No en un modelo.
¿Qué dice internet?
Entonces, esto me llevó a buscar ya que nunca he usado (conscientemente) una capa de servicio. Comencé a leer sobre el patrón de la capa de servicio y el patrón de la unidad de trabajo, pero hasta ahora no puedo decir que esté convencido de que se debe usar una capa de servicio.
Tomemos, por ejemplo, este artículo de Martin Fowler sobre el antipatrón de un modelo de dominio anémico:
Hay objetos, muchos con nombres de nombres en el espacio de dominio, y estos objetos están conectados con las relaciones y la estructura rica que tienen los verdaderos modelos de dominio. El truco se produce cuando observas el comportamiento y te das cuenta de que casi no hay comportamiento en estos objetos, lo que los convierte en poco más que bolsas de captadores y colocadores. De hecho, a menudo estos modelos vienen con reglas de diseño que dicen que no debe poner ninguna lógica de dominio en los objetos de dominio. En cambio, hay un conjunto de objetos de servicio que capturan toda la lógica del dominio. Estos servicios viven sobre el modelo de dominio y usan el modelo de dominio para datos.
(...) La lógica que debería estar en un objeto de dominio es la lógica de dominio: validaciones, cálculos, reglas de negocio, como quiera llamarlo.
Para mí, esto parecía exactamente de qué se trataba la situación: abogaba por la manipulación de los datos de un objeto mediante la introducción de métodos dentro de esa clase que hacen exactamente eso. Sin embargo, me doy cuenta de que esto debería ser de cualquier manera, y probablemente tenga más que ver con cómo se invocan estos métodos (usando un repositorio).
También tuve la sensación de que en ese artículo (ver más abajo), una capa de servicio se considera más como una fachada que delega el trabajo al modelo subyacente, que una capa de trabajo intensivo real.
Capa de aplicación [su nombre para Capa de servicio]: define los trabajos que se supone que debe hacer el software y dirige los objetos de dominio expresivo para resolver problemas. Las tareas de las que es responsable esta capa son significativas para el negocio o necesarias para la interacción con las capas de aplicación de otros sistemas. Esta capa se mantiene delgada. No contiene reglas de negocio o conocimiento, sino que solo coordina tareas y delega el trabajo a colaboraciones de objetos de dominio en la siguiente capa hacia abajo. No tiene un estado que refleje la situación comercial, pero puede tener un estado que refleje el progreso de una tarea para el usuario o el programa.
Lo que se refuerza aquí :
Interfaces de servicio. Los servicios exponen una interfaz de servicio a la que se envían todos los mensajes entrantes. Puede pensar en una interfaz de servicio como una fachada que expone la lógica empresarial implementada en la aplicación (normalmente, la lógica en la capa empresarial) a los consumidores potenciales.
Y aqui :
La capa de servicio debe estar desprovista de cualquier aplicación o lógica empresarial y debe centrarse principalmente en algunas preocupaciones. Debe envolver las llamadas de Business Layer, traducir su Dominio a un lenguaje común que sus clientes puedan entender y manejar el medio de comunicación entre el servidor y el cliente solicitante.
Este es un serio contraste con otros recursos que hablan sobre la capa de servicio:
La capa de servicio debe consistir en clases con métodos que sean unidades de trabajo con acciones que pertenezcan a la misma transacción.
O la segunda respuesta a una pregunta que ya he vinculado:
En algún momento, su aplicación querrá cierta lógica empresarial. Además, es posible que desee validar la entrada para asegurarse de que no se solicite algo malo o que no funcione. Esta lógica pertenece a su capa de servicio.
"Solución"?
Siguiendo las pautas de esta respuesta , se me ocurrió el siguiente enfoque que utiliza una capa de servicio:
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;
}
}
Conclusión
En conjunto, no ha cambiado mucho aquí: el código del controlador se ha movido a la capa de servicio (lo cual es algo bueno, por lo que este enfoque tiene una ventaja). Sin embargo, esto no parece tener nada que ver con mi respuesta original.
Me doy cuenta de que los patrones de diseño son pautas, no reglas establecidas para ser implementadas siempre que sea posible. Sin embargo, no he encontrado una explicación definitiva de la capa de servicio y cómo debería considerarse.
¿Es un medio para simplemente extraer la lógica del controlador y ponerla dentro de un servicio?
¿Se supone que forma un contrato entre el controlador y el dominio?
¿Debería haber una capa entre el dominio y la capa de servicio?
Y, por último pero no menos importante: siguiendo el comentario original
La lógica empresarial realmente debería estar en un servicio. No en un modelo.
¿Es esto correcto?
- ¿Cómo introduciría mi lógica comercial en un servicio en lugar del modelo?