Modelos gruesos vs. Lógica empresarial, ¿de dónde sacas la distinción?

16

Hoy entré en un acalorado debate con otro desarrollador de mi organización sobre dónde y cómo agregar métodos a las clases mapeadas de la base de datos. Usamos sqlalchemy, y una parte importante de la base de código existente en nuestros modelos de base de datos es poco más que una bolsa de propiedades mapeadas con un nombre de clase, una traducción casi mecánica de las tablas de la base de datos a los objetos de Python.

En el argumento, mi posición era que el valor principal de usar un ORM era que se pueden asociar comportamientos y algoritmos de bajo nivel a las clases asignadas. Los modelos son primero las clases y, en segundo lugar, persistentes (podrían ser persistentes utilizando xml en un sistema de archivos, no es necesario que le importe). Su punto de vista era que cualquier comportamiento es "lógica de negocios", y necesariamente pertenece a cualquier lugar que no sea el modelo persistente, que debe usarse solo para la persistencia de la base de datos.

Ciertamente, creo que hay una distinción entre lo que es la lógica de negocios, y debe separarse, ya que tiene cierto aislamiento del nivel inferior de cómo se implementa, y la lógica de dominio, que creo que es la abstracción proporcionada por las clases modelo. discutí en el párrafo anterior, pero estoy teniendo dificultades para señalar qué es eso. Tengo una mejor idea de lo que podría ser la API (que, en nuestro caso, es HTTP "ReSTful"), en el sentido de que los usuarios invocan la API con lo que quieren hacer , distinto de lo que se les permite hacer y cómo se hace.


tl; dr: ¿Qué tipo de cosas pueden o deben ir en un método en una clase asignada cuando se usa un ORM, y qué se debe dejar de lado, para vivir en otra capa de abstracción?

SingleNegationElimination
fuente
Me parece que se trataron dos cuestiones al mismo tiempo, la cuestión de los modelos de persistencia son las clases primero y, en segundo lugar, persistentes (podrían ser persistentes usando xml en un sistema de archivos, no necesita preocuparse). y la razón del código. Por lo general, las cosas se aclaran, al menos para mí, cuando me pregunto por qué está escrito el código, qué requisito obliga a este código. ¿Es el requisito del cliente sobre cómo funcionará el programa o de qué manera elegimos implementarlo? Para mí, lo primero es la lógica de negocios y lo segundo que se llama lógica de dominio. Cómo ayuda esto

Respuestas:

10

Estoy principalmente contigo; su colega parece estar discutiendo ya sea por el antipatrón del modelo de dominio anémico o por duplicar el modelo en un "modelo de persistencia" sin ningún beneficio obvio (estoy trabajando en un proyecto Java donde esto se hizo, y es un dolor de cabeza de mantenimiento masivo, como significa tres veces el trabajo cada vez que algo cambia en el modelo).

¿Qué tipo de cosas pueden o deben ir en un método en una clase asignada cuando se usa un ORM, y qué se debe dejar de lado para vivir en otra capa de abstracción?

Regla general: la clase debe contener una lógica que describa hechos básicos sobre los datos que son verdaderos en todas las circunstancias. La lógica específica de un caso de uso debería estar en otro lugar. Un ejemplo es la validación, hay un artículo interesante de Martin Fowler donde señala que debe considerarse dependiente del contexto.

Michael Borgwardt
fuente
+1, para la segunda parte de tu respuesta. En cuanto a la primera parte, estoy seguro de por qué dice que el modelo de dominio anémico requiere tanto trabajo 'extra' en caso de cambio. Entiendo que el cambio ocurrirá de cualquier manera, pero en varias clases diferentes (lo cual es algo malo), pero es casi la misma cantidad de cambio; ¿Supongo?
NoPuerto
1
@Emmad Kareem: El comentario fue sobre tener modelos de persistencia y dominio de spearate. Agregar una propiedad al modelo requiere agregarlo a dos clases de modelos (en lugar de una), así como a los mapas entre ellos (que en teoría podrían ser automáticos, pero por lo general, quién pensó que era una buena idea tener una el "modelo de persistencia" decide justificar esa separación haciéndolas diferentes, por ejemplo, tener tipos de datos que coincidan más estrechamente con el modelo de tipo DB) de modo que sea 2 + X veces la cantidad de cambio, con X variando entre 0 y horas de pérdida de productividad debido a la oscuridad problemas de mapeo.
Michael Borgwardt
3

Esta es una decisión que realmente depende de su tamaño y escala anticipados de lo que está desarrollando. El enfoque más rígido es limitar los tipos ORM a un componente de acceso a datos y usar POCO en una biblioteca común como tipos referenciados y utilizados por todas las capas. Esto permitiría la separación física futura, así como la separación lógica. También podría decidir que debería existir una capa adicional entre la interfaz de usuario y la capa de lógica de negocios. Esto generalmente se llama una capa Fachada o Interfaz empresarial. Esta capa adicional es donde vive su "código de caso de uso". La capa Facade / BI invoca el código individualmente acoplado (por ejemplo, Facade tiene una función ProcessOrder () que llama a la lógica de negocios 1: M veces para realizar todos los pasos necesarios para procesar realmente el pedido).

Sin embargo, dicho todo esto: muchas veces esta cantidad de arquitectura es simplemente una exageración innecesaria. Por ejemplo, codifique específicamente para un sitio web simple donde no tiene intención de empaquetar sus componentes para su reutilización. Es perfectamente válido crear un sitio web MVC y usar objetos EF para este tipo de solución. Si el sitio necesita escalar más tarde, puede examinar la agrupación o un proceso que a menudo se pierde, llamado "refactorización".

Sean Chase
fuente
3

Solo recuérdele a su colega que no necesita sobreajustar los modelos como si fuera un proyecto Java. Quiero decir, comparar dos objetos persistentes es un comportamiento, pero ninguno está especificado por la capa de persistencia. Entonces la pregunta de la cerveza 6 es: ¿por qué tener clases completamente no relacionadas que describen algo sobre lo mismo? Claro, la persistencia es un aspecto lo suficientemente grande de un modelo para ser tratado por separado, pero no lo suficiente como para garantizar que sea tratado de forma distinta a todo lo demás. Si conduce su automóvil, lo lava o lo revienta, está manipulando su automóvil todo el tiempo.

Entonces, ¿por qué no componer todos estos aspectos diferentes en una sola clase de modelo? Necesita un montón de métodos de clase que se ocupen de objetos persistentes: colóquelos en una clase; tiene un montón de métodos de instancia que se ocupan de la validación; colóquelos en otro. Por último, mezclar los dos y listo! Obtuviste una representación de modelo inteligente, consciente de sí mismo y totalmente contenida allí mismo.

Filip Dupanović
fuente
1

Además de otras respuestas, preste atención a los cavehats ocultos cuando use modelos de dominio rico con un ORM.

Tuve problemas para inyectar servicios polimórficos en las clases de modelos persistentes cuando intentaba lograr algo como el siguiente pseudocódigo:

Person john = new Person('John Doe')
Organisation company = organisation_repository.find('some id')
Employee our_collegue_john = company.hire(john)

En este caso, una organización puede requerir una HRServicedependencia como constructor (por ejemplo). Por lo general, no puede controlar fácilmente la instanciación de sus clases de modelo cuando usa un ORM.

Estaba usando Doctrine ORM y el contenedor de servicio de Symfony. Tuve que parchear el ORM de una manera no tan elegante y no tuve más remedio que separar la persistencia y los modelos comerciales. Todavía no lo he intentado con sqlachemy, pensé. Python podría resultar más flexible que PHP para estas cosas.

abstrus
fuente