¿Debo modificar una entidad con muchos parámetros o con la entidad misma?

7

Tenemos un sistema basado en SOA. Los métodos de servicio son como:

  1. UpdateEntity(Entity entity)

Para entidades pequeñas, todo está bien. Sin embargo, cuando las entidades se vuelven cada vez más grandes, para actualizar una propiedad debemos seguir este patrón en la interfaz de usuario:

  1. Obtener parámetros de la IU (usuario)
  2. Cree una instancia de la entidad, utilizando esos parámetros
  3. Obtener la entidad del servicio
  4. Escriba el código para completar las propiedades sin cambios
  5. Dar la entidad de resultado al servicio

Otra opción que he experimentado en experiencias anteriores es crear métodos de actualización semánticos para cada escenario de actualización. En otras palabras, en lugar de tener un método de actualización global que abarque todo, tuvimos muchos métodos paramétricos ad-hoc. Por ejemplo, para la entidad User , en lugar de tener el método UpdateUser (User user) , teníamos estos métodos:

  1. ChangeUserPassword(int userId, string newPassword)
  2. AddEmailToUserAccount(int userId, string email)
  3. ChangeProfilePicture(int userId, Image image)
  4. ...

Ahora, no sé qué método es realmente mejor y, para cada enfoque, encontramos problemas. Quiero decir, voy a diseñar la infraestructura para un nuevo sistema, y no tengo suficientes razones para elegir cualquiera de estos enfoques. No pude encontrar buenos recursos en Internet, debido a la falta de palabras clave que podría proporcionar. ¿Qué enfoque es mejor? ¿Qué trampas tiene cada una? ¿Qué beneficios podemos obtener de cada uno?

    
pregunta Saeed Neamati 24.06.2012 - 14:03

3 respuestas

8

Su pregunta equivale a elegir el nivel de abstracción correcto para la interfaz de su servicio. Cada sistema del mundo real tiene requisitos diferentes, pero como regla general, prefiero encarecidamente las interfaces de servicio que tengan una vista de alto nivel. Es decir, el segundo enfoque que usted describe, no el primero.

Sin embargo, esto es solo una guía y para tomar una decisión acertada, tendrá que ser capaz de justificarlo con más de "alguien dijo que era mejor así" o "es el enfoque habitual".

Así que vamos a profundizar en los temas relevantes aquí.

Acoplamiento

Su diseño debe permitir que la implementación del servicio y sus clientes se acoplen libremente. El cliente no debería necesitar saber cómo se implementa el servicio, o cómo almacena (a menudo, incluso cómo representa) sus datos. Debería ser posible (en gran medida) cambiar la implementación del servicio sin necesidad de reconstruir o modificar los clientes. El uso de la palabra "Entidad" en su descripción parece implicar que los objetos expuestos a través de la interfaz de servicio corresponden exactamente a las entidades en las que se han descompuesto los datos de la aplicación. Si es así, esto puede indicar que hay un acoplamiento estrecho entre la interfaz del cliente y la implementación del servicio. Sin embargo, a veces esto es permisible, cuando el servicio utiliza definiciones de datos definidas en su propia interfaz (esto puede ayudar con la descomposición, que mencionaré más adelante).

Piense en cómo implementaría un cambio en las estructuras de datos utilizadas en la interfaz; es decir, si las estructuras de datos que se transmiten a través de la interfaz de su servicio deben cambiar, ¿debe realizar un cambio importante (¡torpe!) o su diseño de servicio puede acomodar a clientes, algunos de los cuales esperan la nueva representación y otros la antigua? (La versión de la interfaz es una forma común de permitir esto)

Descomposición

El diseño de la interfaz del servicio generalmente debería permitir la flexibilidad suficiente para que la implementación del servicio pueda dividirse para permitir que parte de la implementación del servicio migre a un diseño diferente. Por ejemplo, debería ser posible que el mantenimiento de los atributos de las personas se mueva de la implementación del servicio anterior a una base de datos LDAP. Aunque supongo que esto es siempre posible, quiero decir que el diseño debería funcionar de tal manera que los clientes no necesariamente deban preocuparse de que esto haya sucedido, y que el la implementación de solo una cantidad razonable de las interfaces del servicio debe ser modificada para permitir esto. Fraseando esto de manera diferente, este tipo de refactorización no debería requerir que se realicen cambios en partes no relacionadas de la implementación del servicio.

Indirección

El nivel de abstracción de su servicio debe elegirse de tal manera que interactúe de una manera fácil de explicar con sistemas en capas. Por ejemplo, debería ser sencillo crear un servicio de almacenamiento en caché que dé prioridad a este servicio y almacene en caché los datos para reducir la carga en el back-end. Igualmente con modificaciones al esquema de autorización. Igualmente, debería ser razonablemente simple crear un back-end simulado (a menudo ofreciendo solo un subconjunto de interfaces) para facilitar las pruebas de los clientes. Igualmente, clientes artificiales para pruebas de carga.

Otros problemas

Hay otros aspectos que deben tenerse en cuenta para algunos sistemas que no siempre son importantes, pero que pueden hacer la vida muy difícil si no los piensa con anticipación. Uno importante aquí es la cuestión de cómo dividir el servicio. Es decir, divida el sistema existente en varias partes, cada una de las cuales trata con algunos de los objetos de datos en el sistema. Por ejemplo, si su sistema acepta correo electrónico, es posible que deba dividirlo en varios backends, cada uno aceptando un correo electrónico para un subconjunto de usuarios (por ejemplo, "a" - "f" en el fragmento 1, "g" - "p" en el fragmento 2, "q" - "t" en el fragmento 3, "u" - "z" en el fragmento 4, "Â" - "Ͱ" en el fragmento 5, y así sucesivamente). Para permitir que los clientes permanezcan sin cambios, este tipo de cambio también suele ir acompañado de la introducción de una capa proxy, cuyo único trabajo es enviar las solicitudes al backend relevante (debería ser posible implementar tantos proxies como sean) necesario para hacer frente a la carga; su tarea no es computacionalmente difícil y no debería requerir golpear un disco para identificar el backend correcto).

    
respondido por el James Youngman 24.06.2012 - 16:54
2

El segundo enfoque de las actualizaciones proporciona más significado semántico a la actualización. Es similar a CQRS ( Separación de responsabilidad de la consulta de comando ) donde proporciona un servicio diferente para Comandos (cambios en el modelo) versus Consultas contra el modelo. Usando este enfoque, puede mitigar la necesidad de manejar la concurrencia optimista porque en lugar de una operación global "este objeto se actualizó", proporciona operaciones distintas que modifican partes específicas de información.

Este enfoque también proporciona un buen punto de entrada para una auditoría más detallada. En lugar de ver al Cliente X actualizado del Usuario A, puede decir que la dirección de facturación principal del Cliente X cambió.

También recomendaría usar un O / RM que proporcione el seguimiento de cambios fuera de la caja para que solo cargue la entidad, realice el cambio y guárdelo. También puede aprovechar diseño controlado por dominio y poner estas operaciones directamente en la entidad. Entonces, en lugar de tener una operación de servicio que manipula la entidad, la capa de servicio se convierte en una fachada que delega la operación directamente a la entidad.

Usando este enfoque, los detalles de su modelo subyacente pueden cambiar sin requerir que la capa de servicio o sus clientes cambien.

    
respondido por el Michael Brown 24.06.2012 - 16:43
0

A menos que esté utilizando un ORM para rastrear los cambios, sería difícil pasar solo las columnas modificadas simplemente porque hay muchas permutaciones de parámetros. Necesitarías escribir un método separado para cada permutación. Esto claramente no es posible.

Ejemplo:

Cliente (id, nombre, apellido)

Para actualizar el registro de un cliente, puede pasar los siguientes parámetros a UpdateCustomer (y sus sobrecargas):

A-firstName

B-firstName, LastName

C-LastName

Este no es un enfoque aceptable, especialmente con un gran número de columnas.

Pasar a toda la entidad también tiene sus inconvenientes, sin embargo, se pueden tolerar:

  1. Es propenso a errores. Debe asegurarse de que cada columna contenga los datos correctos.

  2. Pasa datos que no son necesarios y requerirá una instrucción de actualización con instrucciones SET innecesarias. Además, es posible que deba realizar búsquedas innecesarias en otras tablas para completar los valores en los FK y realizar validaciones innecesarias.

  3. En algunas aplicaciones, no está bien actualizar todos los datos en una entidad, ya que algunos usuarios no tienen el privilegio de hacerlo. Esto es cierto para las aplicaciones bancarias, por ejemplo. En este caso, debe pasar las columnas modificadas, de lo contrario no se realizará la actualización para usuarios sin privilegios.

Entonces, a menos que esté dispuesto a escribir una capa que detecte cambios y genere SQL para ella, el mejor enfoque (en mi opinión) es usar un ORM que haga un seguimiento de los cambios por usted.

    
respondido por el NoChance 24.06.2012 - 18:07

Lea otras preguntas en las etiquetas