2010-01-19 20 views
5

Digamos que tenemos una entidad de raíz agregada del tipo Orden que relaciona clientes y líneas de pedido. Cuando pienso en una entidad de orden, es más natural conceptualizarla como no definida sin una Id. Una orden sin identificación parece estar mejor representada como una solicitud de pedido que como una orden.¿Cómo se debe expresar la adición de una raíz agregada a un repositorio?

Para añadir una orden a un repositorio, por lo general veo a la gente una instancia del orden sin la identificación y luego tener el repositorio completar el objeto:

class OrderRepository 
{ 
    void Add(Order order) 
    { 
     // Insert order into db and populate Id of new order 
    } 
} 

Lo que me gusta de este enfoque es que está añadiendo una Pedir instancia a un OrderRepository. Eso tiene mucho sentido. Sin embargo, la instancia de pedido no tiene un Id. Y, al alcance del consumidor del repositorio, aún no tiene sentido para mí que un pedido no tenga un Id. Podría definir una OrderRequest como una instancia de orden y agregar eso al repositorio, pero eso se siente como derivar una manzana de una naranja y luego agregarla a una lista de naranjas.

Alternativamente, también he visto este enfoque:

class OrderRepository 
{ 
    Order AddOrder(Customer customer) 
     // It might be better to call this CreateOrder 
    { 
     // Insert record into db and return a new instance of Order 
    } 
} 

Lo que me gusta de este enfoque es que una orden es indefinido y sin una identificación. El repositorio puede crear el registro de la base de datos y reunir todos los campos requeridos antes de crear y devolver una instancia de un pedido. Lo que huele aquí es el hecho de que nunca se agrega una instancia de un pedido al repositorio.

De cualquier manera funciona, entonces mi pregunta es: ¿Tengo que vivir con una de estas dos interpretaciones, o hay una mejor práctica para modelar la inserción?

Encontré esta respuesta que es similar, pero para objetos de valor: how should i add an object into a collection maintained by aggregate root. Cuando se trata de un objeto de valor no hay confusión, pero mi pregunta se refiere a una entidad con identidad derivada de una fuente externa (Id de base de datos autogenerada).

+0

Argumento que una orden sin identificación no es una entidad de orden en absoluto, que es de donde proviene su primer pensamiento inicial. Para combatir esto, debes recordar el estado de tus objetos. Mire 'estado' como una pausa en el tiempo. Es decir, una solicitud de página web. Durante la creación de un pedido, sí, no tiene un Id al principio, pero aún no ha terminado su estado: lo agrega en el repositorio, completando así el estado persistente del Pedido(), que ahora es completar. – eduncan911

+0

No estoy en desacuerdo con usted hasta el momento, sin embargo, si usted reclama que una orden sin ID es una entidad por una breve pausa en el tiempo, durante esa pausa no tendrá una identidad discernible. Creo, al menos semánticamente, que esto es una contradicción con la definición de una entidad. –

+0

La identidad no es tan cortante y seca como se podría pensar. En mi fe, nuestras identidades son nuestras almas únicas presentes desde nuestra concepción. Pero para el gobierno, nuestras identidades se pueden establecer a través de más de un medio: certificado de nacimiento, pasaporte, etc. Hay un momento en el que el niño no tiene una identidad oficial para el gobierno, pero el gobierno aún lo ve como una entidad, aunque necesita una identificación. Piense en una orden como un pequeño bebé precioso y en el repositorio como el gobierno (¡de todos modos, los usuarios son más amigables con el usuario!) :) – tuespetre

Respuesta

5

Quisiera comenzar descartando el segundo enfoque. No solo parece contrario a la intuición, también viola varios principios de diseño buenos, como Command-Query Separation y Principle of Least Surprise.

Las opciones restantes dependen de Domain Logic. Si la lógica de dominio dicta que una orden sin un ID no tiene sentido, el ID es un invariante requerido de Orden, y hay que modelar así:

public class Order 
{ 
    private readonly int id; 

    public Order(int id) 
    { 
     // consider a Guard Clause here if you have constraints on the ID 
     this.id = id; 
    } 
} 

en cuenta que al marcar el campo id como readonly lo hemos hecho un invariante No hay forma de que podamos cambiarlo para una instancia de Orden dada. Esto encaja perfectamente con Domain-Driven Design 's Entidad patrón.

Puede aplicar aún más la Lógica de Dominio al poner una Cláusula de Guardia en el constructor para evitar que la ID sea negativa o cero.

En este momento, probablemente se esté preguntando cómo funcionará esto con los ID autogenerados de una base de datos. Bueno, no es así.

No hay una buena forma de asegurarse de que la ID suministrada no esté en uso.

Eso te deja con dos opciones:

  • cambiar el ID de un GUID. Esto permite a cualquier persona que llama proporcionar una identificación única para un nuevo pedido. Sin embargo, esto requiere que use Guids como claves de base de datos también.
  • Cambie la API para que la creación de un nuevo pedido no tome un objeto Order, sino más bien un OrderRequest como usted sugirió: OrderRequest podría ser casi idéntico a la clase Order, menos el ID.

En muchos casos, crear un nuevo pedido es una operación comercial que necesita un modelado específico en cualquier caso, por lo que no veo ningún problema en hacer esta distinción. Aunque Order y OrderRequest pueden ser semánticamente muy similares, ni siquiera tienen que estar relacionados en la jerarquía de tipos.

Incluso podría ir tan lejos como para decir que no deben estar relacionados, porque OrderRequest es un Objeto Valor mientras que orden es una Entidad .

Si se adopta este enfoque, el método AddOrder debe devolver una instancia de pedido (o al menos el ID), porque de lo contrario no podemos conocer el ID del pedido que acabamos de crear. Esto nos lleva de vuelta a la violación de CQS, por lo que tiendo a preferir las guías para las identificaciones de entidad.

+0

¡Gran respuesta! Antes de hacer la pregunta, me inclinaba a cambiar la API para aceptar una Solicitud de pedido, y estoy de acuerdo con usted en que debe ser un objeto de valor no incluido en la jerarquía de Orden. Sin embargo, ahora realmente me gusta su sugerencia Guid. Podría definir un constructor de pedidos que sepa cómo crear una instancia a sí mismo como único, y luego agregarlo al repositorio. Suena perfecto para mí. Sin embargo, me siento incómodo porque esta solución parece estar fuera de la práctica normal. ¿Te has encontrado con muchos proyectos en los que tenías ganas de usar esta solución pero no los permitiste? –

+0

Puede encontrarse con personas que piensan que esta es una mala idea por varias razones. Algunos de ellos están relacionados en su mayoría con los enfoques de Cultivo de aplicaciones para la arquitectura de aplicaciones, pero la resistencia aún puede ser real. En general, hay una (pequeña) sobrecarga de rendimiento en el uso de Guids como claves en lugar de Ints, pero nada que me haya detenido hasta ahora. El argumento más importante contra Guids es si el ID se usa fuera de la aplicación, porque es muy difícil leerlo en voz alta por teléfono (como un ejemplo). El uso de Guids resuelve algunos problemas, pero no todos. Prefiero Guids, pero estoy abierto a otras alternativas también :) –

+0

Si Order y OrderRequest están separados, será un infierno de mantenimiento, ¿no?Es decir, dado que se reflejan completamente, excepto en el campo Id, tendrán que evolucionar conjuntamente todo el tiempo. ¿O me estoy perdiendo algo? – vorou

Cuestiones relacionadas