c# - orientada - Arquitectura DDD para el proyecto ASP.NET MVC2
domain driven design c# (3)
Intento utilizar Domain Driven Development (DDD) para mi nuevo proyecto ASP.NET MVC2 con Entity Framework 4. Después de investigar un poco, presenté las siguientes convenciones de capa con cada capa en su propio proyecto de clase:
MyCompany.Domain
public class User
{
//Contains all the properties for the user entity
}
public interface IRepository<T> where T : class
{
IQueryable<T> GetQuery();
IQueryable<T> GetAll();
IQueryable<T> Find(Func<T, bool> condition);
T Single(Func<T, bool> condition);
T First(Func<T, bool> condition);
T GetByID(int id);
void Delete(T entity);
void Add(T entity);
void Attach(T entity);
void SaveChanges();
}
public interface IUserRepository: IRepository<User> {}
public class UserService
{
private IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
// This class will hold all the methods related to the User entity
}
MyCompany.Repositories
public class UserRepository : IRepository<User>
{
// Repository interface implementations
}
MyCompany.Web -> Este es el Proyecto MVC2
Actualmente mi capa Repositorios contiene una referencia a la capa de Dominio. Desde mi entendimiento, inyectar un UserRepository a la clase UserService funciona muy bien con las pruebas unitarias, ya que podemos pasar en repositorios de usuarios falsos. Entonces, con esta arquitectura, parece que mi proyecto web debe tener una referencia tanto a mi dominio como a las capas de repositorios. Pero, ¿esto es válido? Porque históricamente, la capa de presentación solo tenía una referencia a la capa de lógica de negocios.
Esto es muy válido, y de hecho muy similar a la configuración que tenemos.
Sin embargo, en su pregunta, tiene IRepository
en el proyecto de dominio, lo pondré en su conjunto de repositorios.
Su capa de dominio debe tener las entidades de su dominio y la lógica comercial. La capa Repositorio debe tener la interfaz de depósito genérica y las implementaciones concretas de esto, una para cada raíz agregada.
También tenemos una capa de Servicio que media entre la UI (Controladores) y el Repositorio. Esto permite que una ubicación central coloque una lógica que no pertenece al Repositorio: cosas como Paginación y Validación.
De esta forma, la IU no hace referencia al Repositorio, solo la Capa de Servicio.
También usamos DI para inyectar el EntityFrameworkRepository en nuestra capa de servicio y un repositorio de simulacro en nuestro proyecto de prueba.
Parece que está en el camino correcto, pero dado que parece querer "DDD-All-The-Way", ¿ha considerado implementar el patrón Unidad de trabajo para gestionar varios repositorios que comparten el mismo contexto?
Es posible que desee explorar la forma en que el marco de Sharp Architecture aborda este problema, ya que parece que básicamente está implementando de nuevo las mismas ideas. El tutorial de la aplicación Northwind tiene una agradable discusión sobre estos conceptos.
Solo algunas notas en @ RPM1984 responden ...
Sin embargo, en su pregunta, tiene IRepository en el proyecto de dominio, lo pondré en su conjunto de repositorios.
Si bien no importa tanto dónde ponga las cosas físicamente, es importante recordar que las abstracciones de los repositorios son parte del dominio.
También tenemos una capa de Servicio que media entre la UI (Controladores) y el Repositorio. Esto permite que una ubicación central coloque una lógica que no pertenece al Repositorio: cosas como Paginación y Validación.
Creo que no es una buena idea crear un servicio solo para paginación y validación. Peor aún si se crea artificialmente, simplemente porque ''todo pasa por servicios'' y ''de esa manera no es necesario hacer referencia a repositorios desde la interfaz de usuario''. Los servicios de aplicaciones deben evitarse cuando sea posible.
Para la validación de entrada, ya hay un buen punto para eso. Si utiliza asp.net mvc, considere seguir el patrón y el marco de MVVM que proporcionará formas suficientemente buenas para validar sus modelos de vista de entrada.
Si necesita interacción en varias raíces agregadas (que es una señal de que puede estar perdiendo una nueva raíz agregada): escriba el servicio de dominio .
Parece que está en el camino correcto, pero dado que parece querer "DDD-All-The-Way", ¿ha considerado implementar el patrón Unidad de trabajo para gestionar varios repositorios que comparten el mismo contexto?
Creo que la unidad de trabajo también debería evitarse. He aquí por qué :
Otro ejemplo, UoW es realmente una preocupación de infraestructura ¿por qué traer eso en el dominio ?. Si tiene los límites agregados correctos, no necesita una unidad de trabajo.
Los repositorios no pertenecen al dominio. Los repositorios son sobre la persistencia (es decir, la infraestructura), los dominios son sobre negocios.
Los repositorios tienen una responsabilidad mixta. Por un lado, a las empresas no les importa cómo y dónde persistirán los datos. De otros: el conocimiento de que los clientes pueden encontrarse por el contenido de su carrito de compras es (ejemplo simplificado). Por lo tanto, estoy argumentando que solo las abstracciones deberían ser parte del dominio. Además, estoy en contra del uso directo de repositorios del modelo de dominio porque debería ser persistente ignorante.
Service Layer permite un punto central para ''validación de negocios''.
Los servicios de aplicaciones básicamente son solo fachadas . Y cada fachada es mala si la complejidad agrega problemas de sobrecarga que resuelve.
Aquí hay una mala fachada:
public int incrementInteger(int val){
return val++;
}
Los servicios de aplicaciones no deben contener reglas comerciales. Ese es el punto del diseño impulsado por el dominio: lenguaje ubicuo y código aislado que refleja el negocio de la manera más clara y simple posible.
MVC es bueno para la validación simple, pero no para reglas comerciales complejas (patrón de especificación de pensamiento).
Y para eso debería usarse. Por ejemplo, para verificar si el valor publicado se puede analizar como fecha y hora, lo que se supone que se pasa como argumento al dominio. Lo llamo validación UI. Hay muchos de ellos .
RE UoW: estoy intrigado por esa frase, pero ¿puedes dar más detalles? UOW ES de hecho una preocupación de la infraestructura, pero una vez más esto está en mi nivel de repositorios / datos, no en mi dominio. Todo mi dominio tiene es objetos comerciales / especificaciones.
El patrón de unidad de trabajo nos alienta a aflojar los límites de raíz agregados. Básicamente, nos permite ejecutar transacciones en múltiples raíces. A pesar de que se encuentra fuera del dominio, aún tiene un impacto implícito en las decisiones de dominio y modelado que hacemos. Con bastante frecuencia, esto nos lleva al llamado modelo de dominio anémico.
Una cosa más: capas! = Niveles .
El otro punto que haré es DDD es una guía, no un ser todo-y-todo-todo.
Sí, pero eso no debería usarse como excusa. :)
La otra razón para nuestra capa de servicio es que tenemos una API web. NO queremos que nuestra API web llame a nuestros repositorios, queremos una interfaz fluida y agradable a la que puedan acceder tanto la aplicación web como la API.
Si no hay nada más que recuperar Root y llamarlo, el servicio solo para envolverlo no es necesario. Por lo tanto, sospecho que su verdadero problema es la falta de este aislamiento, por lo tanto, hay una necesidad de orquestar la interacción entre las raíces.
Aquí puede ver algunos detalles de mi enfoque actual.
Otra razón GRANDE para nuestra capa de servicio, es que nuestros repositorios devuelven IQueryable, por lo que no tienen lógica alguna. Nuestra capa de servicio proyecta las expresiones linq, en concretos
Eso no suena bien. El mismo problema: falta de aislamiento.
En este caso, los repositorios no resumen suficientemente la persistencia. Deben tener lógica, una que esté relacionada con la persistencia. Conocimiento de cómo almacenar y recuperar datos. Delegar que "en algún lugar afuera" arruine el punto entero de los repositorios y todo lo que quede, un punto de extensión para burlar el acceso a los datos para las pruebas (lo cual no es malo).
Otra cosa: si los repositorios devuelven IQueryable
procesar, ata automáticamente la capa de servicio con un proveedor de LINQ desconocido (!).
Y algo menos malo (es posible que ni siquiera lo necesite): debido al alto acoplamiento , puede ser bastante difícil cambiar la persistencia a otra tecnología.
No olvide que estamos hablando de preocupaciones técnicas. Estas cosas no tienen nada que ver con el diseño en sí, simplemente lo hacen posible.
Si no usa una capa de servicio, ¿cómo podría (por ejemplo) recuperar una lista de pedidos de un producto? Necesitaría un método en su Repositorio llamado "GetOrdersForProduct", que no creo que sea un buen diseño. La interfaz de su repositorio se vuelve enorme, imposible de mantener. Usamos un Repositorio muy genérico (Buscar, Agregar, Eliminar, etc.). Estos métodos funcionan desde el conjunto de objetos en nuestro modelo. La versatilidad / poder de consulta se adelanta a la capa de servicio
Este es complicado. Dejaré una buena referencia .
Con un proveedor desconocido, quiero decir: realmente no se puede ahora lo que hay debajo de la capa de servicio. Puede ser Linq para objetos, puede ser Linq a xml, Linq a sql, NHIbernate.Linq y un montón de otras implementaciones de proveedores. Lo malo es que no se puede saber qué es compatible.
Esto funcionará bien si es Linq para los objetos y fallará si necesita ser traducido a sql:
customers.Where(c=>{var pickItUp=c.IsWhatever; return pickItUp;});