API Rest y DDD


En mi proyecto usando la metodología DDD.

El proyecto tiene el Acuerdo agregado(entidad). Este agregado tiene muchos casos de uso.

Para este agregado necesito crear una api rest.

Con estándar: crear y eliminar sin problema.

1) CreateDealUseCase(nombre, precio y muchos otros parámetros);

POST /rest/{version}/deals/
{ 
   'name': 'deal123',
   'price': 1234;
   'etc': 'etc'
}

2) DeleteDealUseCase (id)

DELETE /rest/{version}/deals/{id}

Pero, ¿qué hacer con el resto de los casos de uso?

  • HoldDealUseCase (id, razón);
  • UnholdDealUseCase(id);
  • CompleteDealUseCase(id, y muchos otros parámetros);
  • Cancelealusecase (id, amercement, reason);
  • ChangePriceUseCase (id, newPrice, reason);
  • ChangeCompletionDateUseCase (id, newDate, amercement, whyChanged);
  • etc (total 20 casos de uso)...

¿cuáles son las soluciones?

1) Usa verbos :

PUT /rest/{version}/deals/{id}/hold
{ 
   'reason': 'test'
}

Pero! Los verbos no se pueden usar en la url(en la teoría del REPOSO).

2) Use el estado completado (que será después del caso de uso):

PUT /rest/{version}/deals/{id}/holded
{ 
   'reason': 'test'
}

Personalmente para mí se ve feo. ¿Tal vez me equivoque?

3) Use 1 PUT request para todas las operaciones:

PUT /rest/{version}/deals/{id}
{ 
   'action': 'HoldDeal',
   'params': {'reason': 'test'}
}

PUT /rest/{version}/deals/{id}
{ 
   'action': 'UnholdDeal',
   'params': {}
}

Es difícil de manejar en el backend. Además, es difícil de documentar. Desde 1 acción tiene muchas variantes diferentes de solicitudes, de las cuales ya depende de respuestas específicas.

Todas las soluciones tienen inconvenientes significativos.

He leído muchos artículos sobre el RESTO en Internet. En todas partes solo una teoría, cómo estar aquí con mi problema específico?

Author: stalxed, 2016-02-29

3 answers

He leído muchos artículos sobre el RESTO en Internet.

Basado en lo que veo aquí, realmente necesitas ver al menos una de las charlas de Jim Webber sobre REST y DDD

Pero, ¿qué hacer con el resto de los casos de uso?

Ignore la API por un momento - ¿cómo lo haría con formularios HTML?

Presumiblemente tendrías una red la página presenta una representación de Acuerdo, con un montón de enlaces en ella. Un enlace te llevaría al formulario HoldDeal, y otro enlace te llevaría al formulario ChangePrice, y así sucesivamente. Cada uno de esos formularios tendría cero o más campos para llenar, y los formularios cada uno publicaría en algún recurso para actualizar el modelo de dominio.

¿Publicarían todos en el mismo recurso? Tal vez, tal vez no. Todos tendrían el mismo tipo de medios, por lo que si todos estuvieran publicando en el mismo punto final web, tendrías que decodificar el contenido del otro lado.

Dado ese enfoque, ¿cómo implementar su sistema? Bueno, el tipo de medios quiere ser json, basado en sus ejemplos, pero realmente no hay nada malo con el resto.

1) Usa verbos:

Eso está bien.

Pero! Los verbos no se pueden usar en la url(en la teoría del REPOSO).

Um... no. A REST no le importa la ortografía de tus identificadores de recursos. Hay un montón de Mejores prácticas de URI que afirman que los verbos son malos, eso es cierto, pero eso no es algo que se deriva del DESCANSO.

Pero si la gente está siendo tan quisquillosa, se nombra el punto final para el comando en lugar del verbo. (ie: "hold" no es un verbo, es un caso de uso).

Use 1 PUT request para todas las operaciones:

Honestamente, esa tampoco está mal. Sin embargo, no querrá compartir el uri (debido a la forma en que se especifica el método PUT), pero use una plantilla donde los clientes pueden especificar un identificador único.

Aquí está la carne: está construyendo una api sobre los verbos HTTP y HTTP. HTTP está diseñado para transferencia de documentos. El cliente le proporciona un documento que describe un cambio solicitado en su modelo de dominio, y usted aplica el cambio al dominio (o no) y devuelve otro documento que describe el nuevo estado.

Tomando prestado el vocabulario de CQRS por un momento, está publicando comandos para actualizar su dominio modelo.

PUT /commands/{commandId}
{ 
   'deal' : dealId
   'action': 'HoldDeal',
   'params': {'reason': 'test'}
}

Justificación - está poniendo un comando específico (un comando con un Id específico) en la cola de comandos, que es una colección.

PUT /rest/{version}/deals/{dealId}/commands/{commandId}
{ 
   'action': 'HoldDeal',
   'params': {'reason': 'test'}
}

Sí, eso también está bien.

Echa otro vistazo a RESTBucks. Es un protocolo de cafetería, pero toda la api solo está pasando pequeños documentos para avanzar en la máquina de estado.

 21
Author: VoiceOfUnreason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-11 11:26:02

Diseñe su api rest independientemente de la capa de dominio.

Uno de los conceptos clave del diseño impulsado por dominio es acoplamiento bajo entre sus diferentes capas de software. Por lo tanto, cuando diseñas tu api rest, piensas en la mejor api rest que podrías tener. Luego, es la función de la capa de aplicación llamar a los objetos de dominio para realizar el caso de uso requerido.

No puedo diseñar su api rest para usted, porque no se lo que está tratando de hacer, pero aquí hay algunos idea.

Según lo entiendo, usted tiene un recurso de Acuerdo. Como usted ha dicho, la creación / eliminación son fáciles:

  • POST / rest / {version} / deals
  • DELETE /rest/{version}/deals/{id}.

Entonces, quieres "mantener" un trato. No se lo que eso significa, tienes que pensar en lo que cambia en el recurso "Deal". ¿Cambia un atributo? si es así, entonces simplemente estás modificando el recurso de Negocio.

PUT / rest / {version}/deals/{id}

{
    ...
    held: true,
    holdReason: "something",
    ...
}

Lo hace añadir algo? ¿Puedes tener varias retenciones en un Trato? Me suena que "hold" es un sustantivo. Si es feo, busca un sustantivo mejor.

POST / rest/{version}/deals/{id} / holds

{
    reason: "something"
}

Otra solución: olvidar la teoría del DESCANSO. Si crees que tu api sería más clara, más eficiente, más simple con el uso de verbos en la url, entonces por todos los medios, hazlo. Probablemente puedas encontrar una manera de evitarlo, pero si no puedes, no hagas algo feo solo porque es la norma.

Mira twitter api: muchos desarrolladores dicen que twitter tiene una API bien diseñada. ¡Tadaa, usa verbos! ¿A quién le importa, siempre y cuando sea genial y fácil de usar?

No puedo diseñar su api para usted, usted es el único que conoce sus casos de uso, pero diré de nuevo mis dos consejos:

  • Diseñe la api rest por sí misma y luego use la capa de aplicación para llamar a los objetos de dominio apropiados en el orden correcto. Eso es exactamente para lo que está aquí la capa de aplicación.
  • No sigas la normas y teorías ciegamente. Sí, debe tratar de seguir las buenas prácticas y normas tanto como sea posible, pero si no puede dejarlas atrás (después de una cuidadosa consideración, por supuesto)
 11
Author: Kaidjin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-29 13:31:54

Separo los casos de uso (UCs) en 2 grupos: comandos y consultas (CQRS), y tengo 2 controladores REST (uno para comandos y otro para consultas). Los recursos REST no tienen que ser objetos modelo para realizar operaciones CRUD en ellos como resultado de POST/GET/PUT/DELETE. Los recursos pueden ser cualquier objeto que desee. De hecho, en DDD no debe exponer el modelo de dominio a los controladores.

(1) RestApiCommandController: Un método por caso de uso de comando. El recurso RESTO en el URI es el nombre de la clase de comando. El método siempre es POST, porque creas el comando, y luego lo ejecutas a través de un bus de comandos (un mediador en mi caso). El cuerpo de la solicitud es un objeto JSON que asigna las propiedades del comando (los args del UC).

Por ejemplo: http://localhost:8181/command/asignTaskCommand/

@RestController
@RequestMapping("/command")
public class RestApiCommandController {

private final Mediator mediator;    

@Autowired
public RestApiCommandController (Mediator mediator) {
    this.mediator = mediator;
}    

@RequestMapping(value = "/asignTaskCommand/", method = RequestMethod.POST)
public ResponseEntity<?> asignTask ( @RequestBody AsignTaskCommand asignTaskCommand ) {     
    this.mediator.execute ( asigTaskCommand );
    return new ResponseEntity ( HttpStatus.OK );
}

(2) RestApiQueryController: Un método por caso de uso de consulta. Aquí el recurso REST en el URI es el objeto DTO que devuelve la consulta (como el elemento de una colección, o solo). El método es siempre GET, y los parámetros de la consulta UC son parámetros en el URI.

Por ejemplo: http://localhost:8181/query/asignedTask/1

@RestController
@RequestMapping("/query")
public class RestApiQueryController {

private final Mediator mediator;    

@Autowired
public RestApiQueryController (Mediator mediator) {
    this.mediator = mediator;
}    

@RequestMapping(value = "/asignedTask/{employeeId}", method = RequestMethod.GET)
public ResponseEntity<List<AsignedTask>> asignedTasksToEmployee ( @PathVariable("employeeId") String employeeId ) {

    AsignedTasksQuery asignedTasksQuery = new AsignedTasksQuery ( employeeId);
    List<AsignedTask> result = mediator.executeQuery ( asignedTasksQuery );
    if ( result==null || result.isEmpty() ) {
        return new ResponseEntity ( HttpStatus.NOT_FOUND );
    }
    return new ResponseEntity<List<AsignedTask>>(result, HttpStatus.OK);
} 

NOTA: Mediator pertenece a la capa de aplicación DDD. Es el límite de UC, busca el comando / consulta y ejecuta el servicio de aplicación apropiado.

 0
Author: choquero70,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-29 23:18:48