¿Por qué separar el acceso a datos?
Del libro, creo que las dos primeras páginas del capítulo Diseño impulsado por el modelo dan alguna justificación de por qué desea resumir los detalles de implementación técnica de la implementación del modelo de dominio.
- Desea mantener una estrecha conexión entre el modelo de dominio y el código.
- Separar las preocupaciones técnicas ayuda a demostrar que el modelo es práctico para la implementación
- Desea que el lenguaje ubicuo penetre en el diseño del sistema.
Esto parece ser todo con el propósito de evitar un "modelo de análisis" separado que se divorcia de la implementación real del sistema.
Por lo que entiendo del libro, dice que este "modelo de análisis" puede terminar siendo diseñado sin considerar la implementación de software. Una vez que los desarrolladores intentan implementar el modelo comprendido por el lado comercial, forman sus propias abstracciones debido a la necesidad, causando un muro en la comunicación y la comprensión.
En la otra dirección, los desarrolladores que introducen demasiadas preocupaciones técnicas en el modelo de dominio también pueden causar esta división.
Por lo tanto, podría considerar que practicar la separación de preocupaciones, como la persistencia, puede ayudar a proteger contra estos diseños de modelos de análisis divergentes. Si se siente necesario introducir cosas como la persistencia en el modelo, entonces es una señal de alerta. Quizás el modelo no sea práctico para la implementación.
Citando:
"El modelo único reduce las posibilidades de error, porque el diseño es ahora una consecuencia directa del modelo cuidadosamente considerado. El diseño, e incluso el código en sí, tiene la comunicabilidad de un modelo".
De la forma en que interpreto esto, si terminas con más líneas de código que tratan cosas como el acceso a la base de datos, pierdes esa comunicación.
Si la necesidad de acceder a una base de datos es para cosas como verificar la unicidad, eche un vistazo a:
Udi Dahan: los mayores errores que cometen los equipos al aplicar DDD
http://gojko.net/2010/06/11/udi-dahan-the-biggest-mistakes-teams-make-when-applying-ddd/
bajo "Todas las reglas no son iguales"
y
Empleando el patrón del modelo de dominio
http://msdn.microsoft.com/en-us/magazine/ee236415.aspx#id0400119
en "Escenarios para no usar el modelo de dominio", que toca el mismo tema.
Cómo separar el acceso a datos
Cargando datos a través de una interfaz
La "capa de acceso a datos" se ha abstraído a través de una interfaz, a la que se llama para recuperar los datos requeridos:
var orderLines = OrderRepository.GetOrderLines(orderId);
foreach (var line in orderLines)
{
total += line.Price;
}
Pros: La interfaz separa el código de plomería de "acceso a datos", lo que le permite seguir escribiendo pruebas. El acceso a los datos se puede manejar caso por caso, lo que permite un mejor rendimiento que una estrategia genérica.
Contras: El código de llamada debe asumir lo que se ha cargado y lo que no.
Digamos que GetOrderLines devuelve objetos OrderLine con una propiedad ProductInfo nula por razones de rendimiento. El desarrollador debe tener un conocimiento profundo del código detrás de la interfaz.
He probado este método en sistemas reales. Terminas cambiando el alcance de lo que se carga todo el tiempo en un intento de solucionar problemas de rendimiento. Termina mirando detrás de la interfaz para ver el código de acceso a datos para ver qué se carga y qué no.
Ahora, la separación de las preocupaciones debería permitir al desarrollador centrarse en un aspecto del código a la vez, tanto como sea posible. La técnica de interfaz elimina el CÓMO se cargan estos datos, pero no CUÁNTO se cargan los datos, CUÁNDO se cargan y DÓNDE se cargan.
Conclusión: ¡Separación bastante baja!
Carga lenta
Los datos se cargan a pedido. Las llamadas para cargar datos están ocultas dentro del gráfico del objeto en sí, donde acceder a una propiedad puede hacer que se ejecute una consulta SQL antes de devolver el resultado.
foreach (var line in order.OrderLines)
{
total += line.Price;
}
Ventajas: El "CUÁNDO, DÓNDE y CÓMO" del acceso a datos está oculto para el desarrollador, centrándose en la lógica de dominio. No hay código en el agregado que se ocupe de cargar datos. La cantidad de datos cargados puede ser la cantidad exacta requerida por el código.
Contras: cuando te encuentras con un problema de rendimiento, es difícil de solucionar cuando tienes una solución genérica de "talla única". La carga diferida puede empeorar el rendimiento general e implementar la carga diferida puede ser complicado.
Interfaz de roles / Recolección ansiosa
Cada caso de uso se hace explícito a través de una interfaz de rol implementada por la clase agregada, lo que permite manejar las estrategias de carga de datos por caso de uso.
La estrategia de recuperación puede verse así:
public class BillOrderFetchingStrategy : ILoadDataFor<IBillOrder, Order>
{
Order Load(string aggregateId)
{
var order = new Order();
order.Data = GetOrderLinesWithPrice(aggregateId);
return order;
}
}
Entonces su agregado puede verse así:
public class Order : IBillOrder
{
void BillOrder(BillOrderCommand command)
{
foreach (var line in this.Data.OrderLines)
{
total += line.Price;
}
etc...
}
}
BillOrderFetchingStrategy se usa para construir el agregado, y luego el agregado hace su trabajo.
Pros: Permite un código personalizado por caso de uso, lo que permite un rendimiento óptimo. Está en línea con el Principio de segregación de interfaz . No hay requisitos de código complejos. Las pruebas unitarias de agregados no tienen que imitar la estrategia de carga. La estrategia de carga genérica se puede utilizar para la mayoría de los casos (por ejemplo, una estrategia de "cargar todo") y se pueden implementar estrategias de carga especiales cuando sea necesario.
Contras: el desarrollador todavía tiene que ajustar / revisar la estrategia de recuperación después de cambiar el código de dominio.
Con el enfoque de la estrategia de recuperación, es posible que aún se encuentre cambiando el código de recuperación personalizado por un cambio en las reglas comerciales. No es una separación perfecta de preocupaciones, pero terminará siendo más fácil de mantener y es mejor que la primera opción. La estrategia de obtención encapsula los datos de CÓMO, CUÁNDO y DÓNDE se cargan. Tiene una mejor separación de preocupaciones, sin perder flexibilidad, ya que el tamaño único se adapta a todos los enfoques de carga diferida.