domain driven design - orientado - ¿Cuál es una forma práctica de modelar tablas de búsqueda en el Diseño Dirigido por Dominio(DDD)?
domain driven design pdf (5)
Bien, leí un artículo de Mathias Verraes hace un tiempo hablando de esto here . Habla sobre la separación de objetos de valor en el modelo de conceptos que sirven a la interfaz de usuario.
Citando el artículo cuando se le pregunta si modelar Países como entidades u objeto de valor:
No hay nada intrínsecamente incorrecto en modelar países como entidades y almacenarlos en la base de datos. Pero en la mayoría de los casos, esas cosas demasiado complicadas. Los países no cambian a menudo. Cuando el nombre de un país cambia, de hecho, para todos los propósitos prácticos, es un nuevo país. Si un país un día ya no existe, no puede simplemente cambiar todas las direcciones, porque posiblemente el país se dividió en dos países.
Sugirió un enfoque diferente para introducir un nuevo concepto llamado AvailableCountry
:
Estos países disponibles pueden ser entidades en una base de datos, registros en un JSON o incluso simplemente una lista codificada en su código. (Eso depende de si la empresa desea acceder fácilmente a ellos a través de una IU).
<?php
final class Country
{
private $countryCode;
public function __construct($countryCode)
{
$this->countryCode = $countryCode;
}
public function __toString()
{
return $this->countryCode;
}
}
final class AvailableCountry
{
private $country;
private $name;
public function __construct(Country $country, $name)
{
$this->country = $country;
$this->name = $name;
}
/** @return Country */
public function getCountry()
{
return $this->country;
}
public function getName()
{
return $this->name;
}
}
final class AvailableCountryRepository
{
/** @return AvailableCountry[] */
public function findAll()
{
return [
''BE'' => new AvailableCountry(new Country(''BE''), ''Belgium''),
''FR'' => new AvailableCountry(new Country(''FR''), ''France''),
//...
];
}
/** @return AvailableCountry */
public function findByCountry(Country $country)
{
return $this->findAll()[(string) $country];
}
}
Entonces parece que hay una tercera solución que es modelar tablas de búsqueda como objetos de valor y entidades.
Por cierto, asegúrese de revisar la sección de comentarios para algunas discusiones serias sobre el artículo.
Estoy aprendiendo DDD (el libro de Eric Evans está abierto frente a mí) y me he encontrado con un problema para el que no puedo encontrar una respuesta. ¿Qué haces en DDD cuando estás tratando de obtener una lista simple de registros de búsqueda?
Ex.
ID de empleado: 123
EmployeeName: John Doe
Estado: Alaska (menú desplegable)
Condado: Wasilla (menú desplegable: se filtrará según el estado).
Por ejemplo, supongamos que tiene un objeto de dominio Empleado, una interfaz IEmployeeRepository y una clase EmployeeRepository. Esto será utilizado por una IU para mostrar una lista de empleados y detalles individuales. En la interfaz de usuario, desea utilizar un menú desplegable para el estado y el condado donde vive el empleado. Los condados disponibles se filtrarán según el estado elegido.
Lamentablemente, las tablas de la base de datos y la interfaz de usuario se ven muy diferentes. En tblEmployees, contiene State Code = AK y County Code = 02130, no los nombres de estado y condado.
La forma antigua (antes de comenzar esta búsqueda de DDD) sería bastante sencilla, solo crea 2 consultas y usa un DataReader para rellenar las listas desplegables. Debajo de la pantalla en los menús desplegables se encuentra el valor, que se usa automáticamente en las publicaciones del formulario.
Con DDD, sin embargo, no estoy seguro de cómo se supone que debes hacer esto. Primero comencé creando objetos de estado y condado, así como repositorios e interfaces para los repositorios. Sin embargo, escribir 4 clases + 2 interfaces y la plomería en los archivos hbm.xml + Employee Business Objects parece exagerado para solo 2 consultas para 2 menús desplegables. Tiene que haber una mejor manera, ¿no? No cambiaré los registros en las tablas estatales o del condado en el corto plazo, e incluso si lo hiciera, no sería a través de esta aplicación. Entonces, realmente no quiero crear objetos comerciales para el estado y el condado si no es necesario.
La solución más simple que veo es simplemente crear una clase auxiliar con métodos que devuelven diccionarios, como GetStatesAll (), GetState () y GetCounties () y GetCounty (), pero eso simplemente parece incorrecto desde una perspectiva DDD.
Por favor ayuda. ¿Cómo puedo usar DDD sin sobreingeniería solo un par de búsquedas simples?
Solución final Creo que finalmente encontré mi respuesta a través de la experiencia, que era poner el método GetStates () en su propia clase de acceso a datos, aunque no una clase de repositorio. Como solo estaba accediendo solo a lectura, lo lancé a una estructura DTO. Como la base de datos era pequeña, los eché por completo a una sola clase, como Todd describió a continuación.
Mis conclusiones:
- Las tablas de búsqueda NUNCA valoran los objetos, porque las tablas de búsqueda SIEMPRE tienen una identidad. Si no tuvieran una identidad, tendrías duplicados, lo que no tendría mucho sentido.
- La tabla de búsqueda de solo lectura puede tener un repositorio, pero probablemente no lo necesite. El objetivo de un repositorio es reducir la complejidad forzando el acceso solo a través del agregado. Revisar el agregado le proporciona una forma de garantizar el cumplimiento de las reglas comerciales, como no agregar neumáticos si no tiene automóvil.
- Si permite el mantenimiento de CRUD en la tabla de búsqueda, tiene sentido que la tabla de búsqueda tenga su propio repositorio.
- El hecho de que termine almacenando los códigos como estructuras no los convierte en "tipos de valor". Fowler dice en POEAA que una estructura es un tipo de valor. Es cierto, las estructuras son inmutables, por lo que Fowler dice que son "tipos de valor", sin embargo, las estaba usando de manera diferente. Estaba usando structs como una forma liviana de pasar alrededor de los DTO que no pensaba cambiar después de su creación inicial. En verdad, las estructuras que usé tenían, de hecho, identidades, pero dado que eran de solo lectura, funcionaban como estructuras.
- Un patrón que he estado usando y que no veo mucho en otro lado es hacer que los campos de clave primaria sean inmutables. Están establecidos por el constructor, pero son de solo lectura (no son los accesores privados) y no se pueden cambiar una vez que se crea el objeto.
El estado y el condado no son entidades, sino objetos de valor. Ellos no son el sujeto de tu sistema. La forma en que dijiste que trataste estos previamente está bien. ¿Cuándo va a cambiar los registros estatales o del condado en su base de datos, en función de los cambios en el estado de su modelo de dominio? No, entonces estos no necesitarán un repositorio.
Es posible que desee examinar el concepto de Separación de consultas de comando . No me preocuparían por los repositorios tipeados para los valores de búsqueda, pero aún así probablemente usaría clases de tipo DTO sobre datasets, etc ...
Es posible que desee pasar un tiempo leyendo los blogs de Greg Young a partir de este hasta el presente. No habla sobre el llenado de datos de búsqueda específicamente, pero a menudo habla de no manejar la funcionalidad de lectura / informes de su aplicación a través de métodos tipeados en un repositorio.
Estás leyendo el libro equivocado si quieres aprender cómo hacer DDD sin complicarlo demasiado. :-)
La solución más simple que está proponiendo está bien si satisface sus necesidades. Encapsular datos de direcciones en objetos comerciales puede ser tan simple o tan complicado como lo requiera su aplicación. Por ejemplo, el objeto del Estado tiene una relación de uno a varios con el Condado, de modo que el Empleado realmente solo necesita hacer referencia a un Condado si elige modelarlo de esa manera. Solo introduciría ese tipo de complejidad si fuera necesario.
Además, no creo que haya mucho que ganar definiendo interfaces para sus repositorios a menos que haya una posibilidad real de que pueda tener múltiples repositorios para sus objetos.
Usando DDD tengo algo similar con lo siguiente:
interface IAddressService
{
IList<Country> GetCountries ();
IList<State> GetStatesByCountry (string country);
IList<City> GetCitiesByState (string state);
// snip
}
País, estado y ciudad son objetos de valor que provienen de una tabla de búsqueda en la base de datos.