8

mayor parte del tiempo en el código de servicio que cabe tener algo como esto:MVC DDD: ¿Está bien usar repositorios junto con servicios en el controlador?

public SomeService : ISomeService 
{ 
    ISomeRepository someRepository; 
    public Do(int id) 
    { 
     someRepository.Do(id); 
    } 
} 

así que es un poco redundante

así que comenzaron a utilizar los repositorios directamente en el controlador

es esta bien ? ¿Hay alguna arquitectura que esté haciendo esto?

+1

En la práctica, no tengo ejemplos de paso como este en mi código. Dicho esto, uno de los propósitos del repositorio es proporcionar la capacidad de simularlo para fines de pruebas unitarias, por lo que la capa adicional de abstracción puede ser útil por esa sola razón. –

+0

@Robert ¿Qué hay de malo en escribir pruebas contra controladores y repositorios simulados? aparte de burlarse de las dependencias de los controladores, ¿qué más tendría cambiar los servicios? –

+0

@Arnis: supongo que lo que quise decir es que el repositorio abstrae la base de datos de tu código de servicio, por lo que si inyectas una dependencia de repositorio diferente, supongo que puedes llamar a eso un "simulacro". –

Respuesta

5

Pierde la capacidad de tener una lógica comercial en el medio.

No estoy de acuerdo con este.

Si la lógica de negocios está donde debería estar: en el modelo de dominio, llamar al repositorio en el controlador (o mejor) usar el encuadernador de modelos para obtener el método agregado de raíz y llamada me parece perfectamente correcto.

Los servicios de aplicación se deben utilizar cuando hay demasiados detalles técnicos involucrados que podrían desordenar los controladores.


que he visto a varias personas mencionan el uso de quelantes modelo para poner en un acuerdo de recompra últimamente. ¿De dónde viene esta loca idea?

Creo que estamos hablando de 2 cosas diferentes aquí. Sospecho que su 'encuadernador modelo' significa usar el modelo simultáneamente como un modelo de vista y vincular los valores cambiados de la IU directamente a la misma (lo cual no es malo en sí mismo y en algunos casos yo iría por ese camino).

Mi 'modelo de aglomerante' es una clase que implementa 'IModelBinder', que toma repositorio en el constructor (que se inyecta y por lo tanto - se puede ampliar si es necesario el almacenamiento en caché con un poco de composición básica) y lo utiliza antes de la acción se llama a recuperar raíz de agregado y reemplazar int id o Guid id o string slug o whatever argumento de acción con el objeto de dominio real. Al combinar eso con el argumento del modelo de vista de entrada, podemos escribir menos código. Algo como esto:

public ActionResult ChangeCustomerAddress 
(Customer c, ChangeCustomerAddressInput inp){ 
    c.ChangeCustomerAddress(inp.NewAddress); 
    return RedirectToAction("Details", new{inp.Id}); 
} 

En mi código real que es un poco más complejo que causa incluye la validación ModelState y algo de manejo de excepciones que podrían ser lanzada desde el interior del modelo de dominio (extraído en el método de extensión del controlador para su reutilización). Pero no mucho más. Hasta el momento, la acción de controlador más larga es ~ 10 líneas de longitud.

Puede ver la implementación de trabajo (bastante complejo y (para mí) complejo innecesario) here.

¿Estás haciendo aplicaciones CRUD con Linq To Sql o estás intentando algo con lógica de dominio real?

Como puede ver (con suerte), este tipo de enfoque en realidad casi nos obliga a avanzar hacia la aplicación task based en lugar de la basada en CRUD.

Al hacer todo acceso a los datos en su capa de servicios y el uso de COI se puede ganar un montón de beneficios de AOP como el almacenamiento en caché invisible, gestión de transacciones, y la composición fácil de componentes que no puedo imaginar que se obtiene con enlazadores de modelos.

... y tener nueva capa de abstracción que nos invita para mezclar con la infraestructura lógica de dominio y pierden el aislamiento del modelo de dominio.

Por favor enlighten me.

No estoy seguro si lo hice. No creo que esté iluminado yo mismo. :)


Here es mi clase de base de encuadernador actual. Here's una de las acciones del controlador de mi proyecto actual. Y here's "falta" de lógica comercial.

+0

He visto a varias personas mencionar el uso de carpetas modelo para llamar a un repositorio últimamente. ¿De dónde viene esta loca idea? ¿Estás haciendo aplicaciones CRUD con Linq To Sql o intentando algo con lógica de dominio real? Al hacer todo el acceso a los datos en su capa de servicio y usar IOC, puede obtener muchos beneficios de AOP como el almacenamiento en caché invisible, la administración de transacciones y la fácil composición de los componentes que no puedo imaginar que obtenga con los archivos del modelo. Por favor iluminame. – Ryan

+0

@Ryan verifique mi respuesta –

+0

c.ChangeCustomerAddress (inp.Address) ¿Este método contiene una llamada al repositorio para guardar al cliente? En caso afirmativo, ¿cómo se inyecta el repositorio dentro del cliente? – mathieu

2

Si usa repositorios en sus controladores, va directamente de la Capa de datos a la Capa de presentación. Pierdes la habilidad de tener lógica de negocios en el medio.

Ahora, si dice que solo usará los Servicios cuando necesite lógica comercial y que use Repositorios en cualquier otro lugar, su código se convertirá en una pesadilla. La capa de presentación ahora llama tanto a la capa de negocios como a la de datos, y usted no tiene una buena separación de preocupaciones.

Siempre iría por esta ruta: Repositories -> Services -> UI. Tan pronto como no crea que necesita una capa de negocios, los requisitos cambian y tendrá que volver a escribir TODO.

+0

No estoy de acuerdo, Business Logic no es todo o nada. No voy a crear una capa de lógica de negocios en caso de que necesite una lógica empresarial en el futuro. Es pre-optimización. Si se adhiere a los principios SÓLIDOS, entonces se puede tratar fácilmente un cambio en los requisitos. Llamar a los repositorios desde el controlador está perfectamente bien cuando es una operación crud no hay lógica de negocios. Si algo cambia y se necesita lógica, envuelva el repositorio en un servicio y llame al nuevo servicio en el controlador. –

+0

@Chuck ¿Desde dónde creen que los repositorios de llamadas de los controladores prohíben la lógica empresarial compleja? –

+0

@Chuck - Sí, YAGNI. Pero nunca he visto operaciones CRUD sin algún tipo de lógica comercial. – Martin

-2

Incluso con el "modelo de dominio enriquecido", aún necesitará un servicio de dominio para manejar la lógica comercial que involucre varias entidades. Tampoco he visto CRUD sin una lógica comercial, pero en un código de muestra simple. Siempre me gustaría ir por la ruta de Martin para mantener mi código sencillo.

+0

Los servicios no deberían tener 'lógica de negocios'. Para eso están las entidades y los objetos de valor en DDD. –

0

Mis propias prácticas ásperos para DDD/MVC:

controladores
  • son específicos de la aplicación, de ahí que sólo debe contener los métodos específicos de la aplicación, y llamar a métodos de los servicios.
  • todos los métodos de servicio público son las transacciones generalmente atómicas o consultas
  • sólo servicios instanciar & repositorios de llamadas
  • mi dominio define una IContextFactory y un IconText (abstracción con fugas masivas como miembros IconText son IDBSet)
  • cada aplicación tiene una clase-de Composition Root, que está instanciando principalmente una factoría de contexto para pasar a los servicios (se puede usar contenedor DI pero no es un gran problema)

Esto me obliga a mantener mi código de negocio, y datos-ac fuera de mis controladores. ¡Me parece una buena disciplina, dado lo flojo que estoy cuando no sigo lo anterior!

+0

Pero también aprecio el argumento de que algunas aplicaciones no justifican llamar a un Servicio simplemente para llamar a un método muy similar en una sola entidad. Es suficiente, pero luego debe decidir dónde comienza y termina su transacción. – Chalky

1

Aquí está la cosa.

"Business Logic" debe residir en sus entidades y objetos de valor.

Los depósitos se ocupan solo de AggregateRoots. Entonces, usar sus repositorios directamente en sus Controladores parece como si estuviera tratando esa acción como su "servicio". Además, dado que su AggregateRoot solo puede referirse a otras AR por su ID, es posible que deba llamar a uno o más repo. Realmente se pone desagradable muy rápido.

Si va a tener servicios, asegúrese de exponer POCO y no el AggregateRoot real y sus miembros.

Su repositorio no debería tener que realizar ninguna otra operación que no sea crear, recuperar, actualizar y eliminar cosas. Es posible que tenga alguna recuperación personalizada basada en condiciones específicas, pero eso es todo. Por lo tanto, tener un método en su repositorio que coincida con uno en su servicio ... el código huele allí mismo.

Su servicio va dirigido a API. Piensa en esto ... si tuvieras que empacar ese servicio en .dll para que lo use, ¿cómo crearías tus métodos de una manera que me resulte fácil saber qué puede hacer tu servicio? Service.Update (object) no tiene mucho sentido.

Y ni siquiera he hablado de CQRS ... donde las cosas se ponen aún más interesantes.

Su Web Api es solo un CLIENTE de su Servicio. Su servicio puede ser utilizado por otro servicio, ¿verdad? Entonces, piénselo. Lo más probable es que necesite un Servicio para encapsular operaciones en AggregateRoots, usualmente al crearlas o al recuperarlas de un repositorio, hacer algo al respecto y luego devolver un resultado. Generalmente.

Tiene sentido?

+0

tiene sentido, es solo que en muchas aplicaciones para la mayoría de las entidades (tablas db) todo lo que necesitamos es hacer una crud simple, y terminar con un servicio de repo y un controlador con un método con el mismo nombre – Omu

Cuestiones relacionadas