2010-09-17 7 views
11

Estoy trabajando con Doctrine2 por primera vez, pero creo que esta pregunta es lo suficientemente genérica como para no depender de un ORM específico.Usando el patrón del asignador de datos, ¿deberían las entidades (objetos de dominio) conocer al asignador?

caso de que las entidades en un modelo de datos Mapper ser consciente - y uso - la Mapper?

Tengo algunos ejemplos específicos, pero todos parecen reducirse a la misma pregunta general.

Si estoy tratando con datos de una fuente externa - por ejemplo, un User tiene muchas Messages - y la fuente externa simplemente proporciona las pocas entidades (como una fuente RSS), ¿cómo puede $user->addMessage($message) comprobación de duplicados a menos que sea es consciente del Mapper, o 'busca' a través de la colección (parece una tarea ineficiente).

Por supuesto, un controlador o secuencia de comandos de transacción podría verificar si hay duplicados antes de agregar el mensaje al usuario, pero eso no parece del todo correcto y conduciría a la duplicación del código.

Si tengo una gran colección - de nuevo un User con muchas Messages - ¿cómo puede la entidad User proporcionar la limitación y la paginación para la recogida sin tener que hacer proxy a una llamada Mapper?

Una vez más, el controlador o Transacción de secuencias de comandos o lo está utilizando la entidad podría utilizar el asignador directamente para recuperar una colección de la User 's Messages limitados en número, rango de fechas, u otros factores - pero eso también daría lugar a código duplicación.

¿Es la respuesta usando Repositorios y haciendo que la Entidad sea consciente de ellos? (Al menos para Doctrine2, y cualquier concepto análogo utilizado por otros ORM). En ese punto, la entidad aún está relativamente desacoplada de Mapper.

Respuesta

8

Regla # 1: Mantenga su modelo de dominio simple y directo.

Primero, no optimice prematuramente algo porque cree que puede ser ineficiente. Construya su dominio para que los objetos y la sintaxis fluyan correctamente. Mantenga limpias las interfaces: $ user-> addMessage ($ message) es limpio, preciso y sin ambigüedades. Debajo del capó, puede utilizar cualquier cantidad de patrones/técnicas para garantizar que se mantenga la integridad (almacenamiento en caché, búsquedas, etc.). Puede utilizar Servicios para organizar dependencias de objeto (complejas), probablemente exageradas para esto, pero aquí hay una muestra/idea básica.

class User 
{ 
    public function addMessage(Message $message) 
    { 
    // One solution, loop through all messages first, throw error if already exists 
    $this->messages[] $message; 
    } 
    public function getMessage() 
    { 
    return $this->messages; 
    } 
} 
class MessageService 
{ 
    public function addUserMessage(User $user, Message $message) 
    { 
    // Ensure unique message for user 
    // One solution is loop through $user->getMessages() here and make sure unique 
    // This is more or less the only path to adding a message, so ensure its integrity here before proceeding 
    // There could also be ACL checks placed here as well 
    // You could also create functions that provide checks to determine whether certain criteria are met/unmet before proceeding 
    if ($this->doesUserHaveMessage($user,$message)) { 
     throw Exception... 
    } 
    $user->addMessage($message); 
    } 
    // Note, this may not be the correct place for this function to "live" 
    public function doesUserHaveMessage(User $user, Message $message) 
    { 
    // Do a database lookup here 
    return ($user->hasMessage($message) ? true 
    } 
} 
class MessageRepository 
{ 
    public function find(/* criteria */) 
    { 
    // Use caching here 
    return $message; 
    } 
} 

class MessageFactory 
{ 
    public function createMessage($data) 
    { 
    // 
    $message = new Message(); 
    // setters 
    return $message; 
    } 
} 

// Application code 
$user = $userRepository->find(/* lookup criteria */); 
$message = $messageFactory->create(/* data */); 
// Could wrap in try/catch 
$messageService->sendUserMessage($user,$message); 

He estado trabajando con Doctrine2 también. Los objetos de entidad de dominio son solo objetos ... no deberían tener idea de su origen, el modelo de dominio simplemente los administra y los pasa a las diversas funciones que los administran y manipulan.

Mirando hacia atrás, no estoy seguro de haber respondido completamente a su pregunta. Sin embargo, no creo que las entidades mismas tengan acceso a los mapeadores. Crear servicios/repositorios/Lo que sea para operar en los objetos y utilizar las técnicas apropiadas en esas funciones ...

No sobreiniciar desde el inicio tampoco. Mantenga su dominio enfocado en su objetivo y refactorice cuando el rendimiento sea realmente un problema.

+0

Sí, estoy con usted en los patrones de diseño, y recorrer todos los mensajes es la solución simple, pero eso significa (asumiendo una gran cantidad de mensajes) que está cargando todos los registros del almacenamiento cuando una simple consulta daría el mismo resultado. –

+0

Sí, por eso mencioné posiblemente el uso de la función doesUserHaveMessage (o algo similar). Eso podría ser donde se realiza la consulta. Aún mantiene sus entidades limpias y enfocadas, pero le permite mantener su dominio constante. Además, puede comenzar con una implementación simple/trivial (loop through array), pero luego refactorizar más tarde cuando el rendimiento lo requiera ... siempre y cuando su interfaz permanezca igual, ningún otro código tiene que cambiar. – jsuggs

1

IMO, una entidad no debe tener en cuenta de dónde viene, quién la creó y cómo poblar sus entidades relacionadas. En el ORM utilizo (mi) Soy capaz de definir uniones entre dos tablas y limitar sus resultados mediante la especificación (en C#):

SearchCriteria sc = new SearchCriteria(); 
sc.AddSort("Message.CREATED_DATE","DESC"); 
sc.MaxRows = 10; 
results = Mapper.Read(sc, new User(new Message()); 

que dará lugar a una unión que está limitada a 10 artículos, ordenados por fecha crear mensaje Los elementos del Mensaje se agregarán a cada Usuario. Si escribo:

results = Mapper.Read(sc, new Message(new User()); 

la unión está invertida.

Por lo tanto, es posible hacer que las Entidades desconozcan por completo el asignador.

+0

Sí, tiendo a querer que la Entidad se desacople de, bueno, cualquier cosa. Su método es más o menos lo que quise decir, "... Controlador o secuencia de comandos de transacción o lo que sea que esté usando la Entidad podría usar el asignador directamente para recuperar una colección de los mensajes del usuario limitados por ..." ¿Qué pasa con el problema de verificar si un 'Mensaje' ya existe? Solo hazlo fuera de la Entidad? –

+0

Supongo que está consumiendo mensajes de un servicio externo (fuente RSS como usted menciona). En este caso, puede buscar el último mensaje recibido; si no lo encuentras, añádelo. Siga haciendo esto con los otros mensajes hasta que encuentre uno que exista, eso significa que ya los alcanzó (a menos que no entienda sus requisitos) –

+0

ávio Eso funcionaría, pero sucedería fuera de la Entidad, ¿correcto? Aunque quiero mantener mi Entity desacoplada, también parece que el código para agregar un 'Mensaje' debe estar en la Entidad (para evitar la duplicación del código). –

1

Aquí está la razón: confianza. No puede confiar en los datos para actuar en beneficio del sistema. Solo puedes confiar en que el sistema actuará sobre los datos. Esto es fundamental en la lógica de programación.

Digamos que algo desagradable se deslizó en los datos y fue diseñado para XSS. Si un fragmento de datos está realizando acciones o si se evalúa, entonces el código XSS se mezcla en cosas y se abrirá un agujero de seguridad.

¡No deje que la mano izquierda sepa lo que hace la mano derecha!(sobre todo porque no desea saber)

+0

Entonces, ¿cuál es la mejor manera de manejar las situaciones genéricas que describo? Solo guárdelo en el script de control/transacción/lo que sea * usa * la entidad? –

+0

Introdúcelo en una nueva clase de control. Siempre es mejor pensar en la codificación como si estuvieras construyendo un montón de herramientas. Nunca le pondrías un martillo en el brazo a alguien; sin embargo, les daría la oportunidad de elegir uno y usarlo. Lo mismo ocurre con sus datos. Debes ajustar el código para que pueda realizar ciertas acciones válidas según los datos, pero debe estar limitado por tus reglas. Cuando permite que los datos controlen el sistema, le da a los datos la posibilidad de dañar el sistema. – Geekster

+0

Supongo que considero la Entidad (o Modelo) como algo que representa y valida los datos. –

Cuestiones relacionadas