¿Las API RESTful tienden a fomentar modelos de dominio anémico?

34

Estoy trabajando en un proyecto en el que estamos tratando de aplicar tanto el diseño basado en dominio como REST a una arquitectura orientada a servicios. No nos preocupamos por el 100% de cumplimiento de REST; probablemente sería mejor decir que estamos tratando de construir API HTTP orientadas a recursos (~ Nivel 2 del modelo de madurez REST de Richardson). Sin embargo, estamos tratando de mantenernos alejados del uso de solicitudes HTTP al estilo RPC, es decir, intentamos implementar nuestros verbos HTTP de acuerdo con RFC2616 en lugar de POSThacerlo IsPostalAddressValid(...), por ejemplo.

Sin embargo, un énfasis en esto parece estar a expensas de nuestro intento de aplicar un diseño basado en dominio. Con solamente GET, POST, PUT, DELETEy algunos otros métodos de uso poco frecuente, que tienden a crear servicios sucia, sucia y los servicios tienden a tener modelos de dominio anémicos.

POST: Reciba los datos, valídelos, vuélvalos a los datos. GET: Recupere los datos, devuélvalos. No hay lógica de negocios real allí. También usamos mensajes (eventos) entre los servicios, y me parece que la mayor parte de la lógica de negocios termina construyéndose alrededor de eso.

¿Están REST y DDD en tensión en algún nivel? (¿O estoy malinterpretando algo aquí? ¿Tal vez estamos haciendo algo más mal?) ¿Es posible construir un modelo de dominio sólido en una arquitectura orientada a servicios mientras evito las llamadas HTTP de estilo RPC?

Kazark
fuente
1
POST fue diseñado deliberadamente para ser "intencionalmente vago"; El resultado de un POST es específico de la implementación. ¿Qué le impide hacer lo que hacen Twitter y otros diseñadores de API, y definir cada método POST en la parte no CRUD de su API de acuerdo con sus propios requisitos específicos?
Robert Harvey
@RobertHarvey Hemos interpretado POST como una creación. Mirando el estándar nuevamente, quizás eso sea demasiado simplista. Por ejemplo, ¿cree que la POST para hacerlo IsPostalAddressValid(...)encajaría con "Proporcionar un bloque de datos, como el resultado de enviar un formulario, a un proceso de manejo de datos"?
Kazark
Esto se debe a que no hay un verbo CREATE (y tampoco hay un verbo UPDATE). Sostengo que esos verbos faltan en el estándar (por cualquier razón), razón por la cual debe cooptar POST para "todo lo demás". Su "proceso de manejo de datos", en este caso, es el proceso que examina la dirección postal y devuelve un valor correspondiente al resultado de ese análisis.
Robert Harvey
1
@RobertHarvey: Creo que POST y PUT / PATCH es simplemente el verbo CREAR y ACTUALIZAR que has estado esperando. Solo se nombra de manera diferente, por lo que el verbo todavía tiene sentido, incluso en un diseño no RESTful.
Lie Ryan
@LieRyan: Te lo concederé. Solo creo que CRUD implica modelos de datos anémicos por definición. Puede llevar algo de comportamiento si, por ejemplo, está en la M de MVC, pero ciertamente no en sistemas heterogéneos. Para todo lo demás excepto CRUD, necesitas POST.
Robert Harvey

Respuestas:

38

Primera ley de Martin Fowler de sistemas distribuidos: "¡No distribuyas tus objetos!" Las interfaces remotas deben ser de grano grueso y las interfaces internas de grano fino. A menudo, el modelo de dominio rico solo se aplica dentro de un contexto acotado .

REST API separa dos contextos diferentes, ambos con sus propios modelos internos. Los contextos se comunican a través de la interfaz de grano grueso (REST API) utilizando objetos "anémicos" (DTO).

En su caso, parece que está tratando de extender un contexto sobre un límite que es REST API. Esto puede conducir a una interfaz remota de grano fino o un modelo anémico. Dependiendo de su proyecto, puede o no ser un problema.

simoraman
fuente
1
Fowler tiene muchas buenas ideas, pero no olvidemos qué desastre fueron las especificaciones e implementaciones originales de EJB. Fue solo después de que descubrieron que las llamadas a métodos de bajo nivel para cada operación menor como getName () era una pesadilla de carga / red. Las interfaces de grano grueso se convirtieron en el camino a seguir y con él el concepto de que se enviaron y recibieron enteros gráficos / mensajes de entidad en el contexto verbo + sustantivo.
Darrell Teague
9

POST fue diseñado deliberadamente para ser "intencionalmente vago"; El resultado de un POST es específico de la implementación. ¿Qué le impide hacer lo que hacen Twitter y otros diseñadores de API, y definir cada método POST en la parte no CRUD de su API de acuerdo con sus propios requisitos específicos? POST es el verbo general. Úselo cuando ninguno de los otros verbos encaja bien con la operación que desea realizar.

Para decirlo de otra manera, su pregunta podría plantearse igualmente como "¿Los objetos 'inteligentes' fomentan el diseño de estilo RPC?" Incluso Martin Fowler (quien acuñó el término "Modelo de dominio anémico") reconoce que los DTO desnudos tienen algunos beneficios:

Poner el comportamiento en los objetos del dominio no debe contradecir el enfoque sólido de usar capas para separar la lógica del dominio de cosas tales como la persistencia y las responsabilidades de presentación. La lógica que debería estar en un objeto de dominio es la lógica de dominio : validaciones, cálculos, reglas de negocio, como quiera llamarlo.

Con respecto al modelo de madurez de Richardson , puede llegar al nivel 3 sin preocuparse por los "modelos de dominio anémico". Recuerde, nunca va a transferir el comportamiento al navegador (a menos que planee inyectar algún Javascript a través de sus modelos).

REST se trata principalmente de independencia de la máquina; implemente el modelo REST en la medida en que desee que sus puntos finales representen recursos, y para que los consumidores de su API puedan acceder y mantener fácilmente esos recursos de manera estándar. Si eso parece anémico, que así sea.

Ver también
Necesito más verbos

Robert Harvey
fuente
Creo que eso ciertamente aborda el lado RFC2616 de la pregunta. ¿Qué pasa con el hecho de que estamos tratando de estar orientados a los recursos, es decir, al menos tratando de alcanzar el Nivel 2 en el modelo de madurez de Richardson para REST?
Kazark
1
Leí martinfowler.com/articles/richardsonMaturityModel.html . Puede llegar al nivel 3 sin preocuparse por los "Modelos de dominio anémico". Recuerde, nunca va a transferir el comportamiento al navegador (a menos que planee inyectar algún Javascript a través de sus modelos).
Robert Harvey
4

REST API es solo un tipo de capa de presentación. No tiene nada que ver con el modelo de dominio.

La pregunta que publicó proviene de su confusión de que de alguna manera necesita adaptarse el uno al otro. Usted no

Usted asigna su modelo de dominio a su API REST de la misma manera que asigna su modelo de dominio a un RDBMS a través de un ORM: tiene que haber esta capa de asignación.

Dominio ← ORM →
Dominio RDBMS ← Mapeo REST → API REST

Elnur Abdurrakhimov
fuente
3

En mi humilde opinión, no creo que tiendan a fomentar los modelos de dominio anémico (ADM), pero requieren que te tomes un tiempo y pienses bien.

En primer lugar, creo que la característica principal de los ADM es que tienen poco o ningún comportamiento en ellos. Eso no quiere decir que el sistema no tenga comportamiento, solo que generalmente está en algún tipo de clase de Servicio (consulte http://vimeo.com/43598193 ).

Y, por supuesto, si el comportamiento no existe en el ADM, ¿qué existe? La respuesta, por supuesto, son los datos. Y entonces, ¿cómo se correlaciona con REST API? Bueno, presumiblemente los datos se asignan al contenido del recurso, y el comportamiento se asigna a los verbos HTTP.

Entonces, tiene todo lo que necesita para construir un modelo de dominio rico, solo tiene que poder ver cómo los verbos HTTP se asignan a las operaciones de dominio en los datos, y luego colocar esas operaciones en las mismas clases que encapsulan sus datos.

Creo que las personas tienden a encontrarse con problemas es que les resulta difícil ver cómo los verbos HTTP se asignan a su comportamiento de dominio cuando el comportamiento está más allá del CRUD simple, es decir, cuando hay efectos secundarios en otras partes del dominio más allá del recurso modificado por la solicitud HTTP. Una forma de resolver ese problema es con los eventos de dominio ( http://www.udidahan.com/2009/06/14/domain-events-salvation/ ).

RibaldEddie
fuente
3

Este artículo está bastante relacionado con el tema y creo que responde a su pregunta.

Un concepto central que creo que responde muy bien a su pregunta, se resume en el siguiente párrafo del artículo mencionado:

"Es muy importante distinguir entre recursos en API REST y entidades de dominio en un diseño dirigido por dominio. El diseño dirigido por dominio se aplica al lado de la implementación de las cosas (incluida la implementación API) mientras que los recursos en API REST dirigen el diseño y el contrato API. Recurso API la selección no debe depender de los detalles de implementación del dominio subyacente ".

Majix
fuente
1

Varias implementaciones razonablemente exitosas que he visto / construido responden a la pregunta de cómo mezclan la metáfora verbo + sustantivo utilizando métodos 'amigables para los negocios' de grano grueso que actúan sobre las entidades.

Entonces, en lugar del getName()método / servicio (condenado) , exponga getPerson(), pasando cosas como identificador-tipo / ID, devolviendo toda la Personentidad.

Dado que los comportamientos de la entidad Persona en dicho contexto no pueden transmitirse adecuadamente (ni quizás deberían estar en un contexto centrado en los datos como este), es perfectamente razonable definir un modelo de datos (versus Objeto) para los pares de solicitud / respuesta de los servicios.

Los servicios y los verbos definidos agregarán algunos comportamientos, controles e incluso reglas de transición de estado para las entidades permitidas por el dominio. Por ejemplo, habría una lógica específica de dominio sobre lo que sucede en la transferPerson()llamada de servicio, pero la interfaz misma solo definiría las entidades / datos de entrada / salida sin definir SUS comportamientos internos.

No estaría de acuerdo con los autores que dirían, por ejemplo, que una implementación del verbo de transferencia pertenece a la clase Persona o está asociada con un servicio centrado en la Persona. De hecho, el método de transferencia para a Persony sus opciones (en este simple ejemplo) estaría mejor definido por a Carrier, en el que es Personposible que ni siquiera sepan qué métodos de transferencia están disponibles o cómo se realiza la transferencia (quién sabe cómo funcionan los motores a reacción). de todas formas).

¿Esto hace que la Personentidad sea anémica? No lo creo.

Puede / debe haber lógica sobre cosas específicas de la Persona que son internas a la Persona, como su estado de salud, que no debe ser definida por una clase externa.

Sin embargo, dependiendo de los casos de uso, es completamente aceptable que una clase de entidad no tenga comportamientos importantes / relevantes en ciertos sistemas, como un servicio de asignación de asiento en un sistema de transporte. Tal sistema puede implementar servicios basados ​​en REST que tratan con instancias de Persona e identificadores asociados pero nunca definen / implementan sus comportamientos internos.

Darrell Teague
fuente
Buenos puntos --- esto realmente trae una nueva perspectiva que otras respuestas aún no tenían.
Kazark
0

¿Su problema es que está tratando de agrupar su modelo en el conjunto básico de verbos, utilizando POST tanto como sea posible?

No es necesario. Sé que para la mayoría de las personas, REST significa POST, GET, PUT y DELETE, pero el http rfc dice:

El conjunto de métodos comunes para HTTP / 1.1 se define a continuación. Aunque este conjunto se puede ampliar, no se puede suponer que métodos adicionales compartan la misma semántica para clientes y servidores extendidos por separado.

Y los sistemas como SMTP utilizan el mismo estilo de métodos basados ​​en verbos pero con un conjunto totalmente diferente.

Por lo tanto, no hay ninguna razón por la que tenga que usar estos, puede usar cualquier conjunto de verbos que desee (sin embargo, realmente encontrará que puede hacer todo lo que necesita en los 4 básicos con un poco de reflexión). Lo que distingue a REST de los otros mecanismos es su forma sin estado y consistente de implementar estos verbos. No debe intentar implementar el sistema de paso de mensajes entre los niveles, ya que básicamente no está haciendo REST, sino que está haciendo un mecanismo de paso de mensajes, RPC o cola de mensajes que indudablemente le hará perder los beneficios de REST (es decir, el simplicidad que hace que funcione realmente bien en una conexión http).

Si desea un protocolo de mensajería complejo y con todas las funciones, compílelo (si puede hacerlo a través de la web, hay una razón por la cual REST es tan popular), pero de lo contrario intente atenerse al diseño arquitectónico de REST.

gbjbaanb
fuente