Permítanme comenzar primero disculpándome por la duración de la publicación, pero realmente quería transmitir tantos detalles por adelantado para que no me tome su tiempo yendo y viniendo en los comentarios.
Estoy diseñando una aplicación siguiendo un enfoque DDD y me pregunto qué orientación puedo seguir para determinar si una Raíz Agregada debería contener otro AR o si deberían dejarse como AR independientes, "independientes".
Tomemos el caso de una aplicación simple de reloj de tiempo que permite a los empleados entrar o salir del día. La interfaz de usuario les permite ingresar su ID de empleado y PIN, que luego se valida y se recupera el estado actual del empleado. Si el empleado está actualmente registrado, la interfaz de usuario muestra un botón de "Salida de reloj"; y, por el contrario, si no están sincronizados, el botón dice "Clock In". La acción realizada por el botón corresponde también al estado del empleado.
La aplicación es un cliente web que llama a un servidor de fondo expuesto a través de una interfaz de servicio RESTful. Mi primer paso para crear URL intuitivas y legibles resultó en los siguientes dos puntos finales:
http://myhost/employees/{id}/clockin
http://myhost/employees/{id}/clockout
NOTA: Estos se utilizan después de que la ID y PIN del empleado se hayan validado y se haya pasado un "token" que representa al "usuario" en un encabezado. Esto se debe a que existe un "modo gerente" que permite que un gerente o supervisor registre o desconecte a otro empleado. Pero, por el bien de esta discusión, estoy tratando de mantenerlo simple.
En el servidor, tengo un ApplicationService que proporciona la API. Mi idea inicial para el método ClockIn es algo como:
public void ClockIn(String id)
{
var employee = EmployeeRepository.FindById(id);
if (employee == null) throw SomeException();
employee.ClockIn();
EmployeeRepository.Save();
}
Esto parece bastante sencillo hasta que nos damos cuenta de que la información de la tarjeta de tiempo del Empleado en realidad se mantiene como una lista de transacciones. Eso significa que cada vez que llamo a ClockIn o ClockOut, no estoy cambiando directamente el estado del Empleado, sino que agrego una nueva entrada en la Hoja de horas del empleado. El estado actual del Empleado (registrado o no) se deriva de la entrada más reciente en el TimeSheet.
Entonces, si voy con el código que se muestra arriba, mi repositorio debe reconocer que las propiedades persistentes del Empleado no han cambiado, pero que se agregó una nueva entrada en la Hoja de tiempo del Empleado y se inserta en el almacén de datos.
Por otro lado (y aquí está la última pregunta de la publicación), TimeSheet parece que es una Raíz Agregada, así como también tiene identidad (el ID del empleado y el período) y podría implementar fácilmente la misma lógica que TimeSheet. (ID de empleado).
Me encuentro debatiendo los méritos de los dos enfoques y, como se indicó en el párrafo inicial, me pregunto qué criterios debería evaluar para determinar qué enfoque es más adecuado para el problema.
fuente
Respuestas:
Me inclinaría a implementar un servicio para el seguimiento del tiempo:
No creo que sea responsabilidad de la hoja de tiempo entrar o salir, ni es responsabilidad de los empleados.
fuente
Las raíces agregadas no deben contenerse entre sí (aunque pueden contener ID para otros).
Primero, ¿TimeSheet es realmente una raíz agregada? El hecho de que tenga identidad la convierte en una entidad, no necesariamente en un AR. Mira una de las reglas:
Defina la identidad de TimeSheet como ID de empleado y período de tiempo, lo que sugiere que TimeSheet es parte de Employee, siendo el período de tiempo la identidad local. Dado que esta es una aplicación de reloj de tiempo , ¿es el propósito principal de Employee ser un contenedor para TimeSheets?
Suponiendo que tiene que tener AR, ClockIn y ClockOut se parecen más a las operaciones de TimeSheet que a las de Employee. Su método de capa de servicio podría verse así:
Si realmente necesita rastrear el estado registrado tanto en Employee como en TimeSheet, eche un vistazo a los Eventos de dominio (no creo que estén en el libro de Evans, pero hay numerosos artículos en línea). Se vería algo así como: Employee.ClockIn () provoca el evento EmployeeClockedIn, que un controlador de eventos recoge y, a su vez, llama a TimeSheet.LogEmployeeClockIn ().
fuente
Una raíz agregada nunca debe contener otra raíz agregada.
En su descripción, tiene una entidad Empleado , y también una entidad Hoja de tiempo. Estas dos entidades son distintas, pero podrían incluir referencias entre sí (por ejemplo, esta es la hoja de tiempo de Bob).
Eso es modelado básico.
La pregunta raíz agregada es ligeramente diferente. Si estas dos entidades son transaccionalmente distintas entre sí, entonces se pueden modelar correctamente como dos agregados distintos. Por transaccionalmente distinto, quiero decir que no existe una empresa invariable que requiera conocer el estado actual del Empleado y el estado actual de TimeSheet simultáneamente.
Según lo que describe, Timesheet.clockIn y Timesheet.clockOut nunca necesitan verificar ningún dato en Employee para determinar si el comando está permitido. Entonces, para este gran problema, dos AR diferentes parecen razonables.
Otra forma de considerar los límites de AR sería preguntar qué tipos de ediciones se pueden realizar simultáneamente. ¿Se le permite al gerente registrar la salida del empleado mientras que al mismo tiempo RRHH está jugando con el perfil del empleado?
La identidad solo significa que es una entidad; es la empresa invariable la que determina si debe considerarse una raíz agregada separada.
Tal vez, el agregado debería. Eso no significa necesariamente que la hoja de tiempo debería.
Es decir, si las reglas para modificar una Hoja de tiempo dependen del estado actual de la entidad Empleado, entonces Empleado y Hoja de tiempo definitivamente deben ser parte del mismo agregado, y el conjunto en su conjunto es responsable de garantizar que las reglas sean seguido.
Un agregado tiene una entidad raíz; identificarlo es parte del rompecabezas. Si un empleado tiene más de una hoja de tiempo, y ambas son parte del mismo agregado, entonces la hoja de tiempo definitivamente no es la raíz. Lo que significa que la aplicación no puede modificar o enviar comandos directamente a la hoja de tiempo; deben enviarse al objeto raíz (presumiblemente el Empleado), que puede delegar parte de la responsabilidad.
Otra comprobación sería considerar cómo se crean las hojas de tiempo. Si se crean implícitamente cuando un empleado registra, entonces esa es otra pista de que son una entidad subordinada en conjunto.
Por otro lado, es poco probable que sus agregados tengan su propio sentido del tiempo. En cambio, se les debe pasar el tiempo
fuente