Estructura de la API ServiceStack recomendada

Estoy tratando de encontrar la mejor manera de estructurar nuestra API; tenemos Revisiones que hemos configurado en una estructura REST estándar (enumere una, liste todas, cree, actualice, etc.). Donde los ejemplos no encajan del todo es que cada revisión se puede vincular a uno o más tipos, por ejemplo, Evento, Ubicación o Cosa.

Mi pensamiento es que las URL estarían en la línea de: / event / reviews / (o al revés de esto eg / reviews / event /) / location / reviews / / thing / reviews /

El problema que puedo ver sin embargo es el “GET” para cada uno de estos debe devolver el objeto principal, es decir, un evento.

Entonces, usando ServiceStack, ¿cuál es la mejor manera de manejar este escenario? ¿Es para crear un servicio personalizado para cada solicitud de datos en lugar de abusar de la configuración REST lista para usar o me he perdido algo más fundamental?

En primer lugar, la “mejor” solución es un término bastante subjetivo. Por lo general, apostaré por soluciones SECAS, reutilizables y de rendimiento que promuevan el mínimo esfuerzo, la fricción y la sintonía, mientras que otras pueden definir “lo mejor” en la cercanía de los principios de REST. Entonces obtendrás respuestas variadas dependiendo de cuáles sean los objectives. Solo puedo ofrecer cómo me gustaría abordarlo.

Las implementaciones del servicio ServiceStack se desacoplan de sus rutas personalizadas

Una cosa a tener en cuenta es cómo definir y diseñar sus servicios en ServiceStack son bastante desacoplados en la forma de exponerlos, ya que puede exponer sus servicios bajo cualquier ruta personalizada. ServiceStack fomenta un diseño basado en mensajes por lo que debe darle a cada operación un mensaje distinto.

Use una estructura Url lógica / jerárquica

Utilizaría una estructura de Url lógica que aspiro a representar el identificador de un nombre, que está estructurado jerárquicamente, es decir, el camino padre categoriza su recurso y le da un contexto significativo. Entonces, en este caso, si desea exponer Eventos y revisar mi inclinación, debe ir con la siguiente estructura de URL:

/events //all events /events/1 //event #1 /events/1/reviews //event #1 reviews 

Cada uno de estos identificadores de recursos puede tener cualquier Verbo HTTP aplicado a ellos

Implementación

Para la implementación, generalmente sigo un diseño basado en mensajes y agrupo todas las operaciones relacionadas en función del tipo de respuesta y el contexto de llamada. Para esto, haría algo como:

 [Route("/events", "GET")] [Route("/events/category/{Category}", "GET")] //*Optional top-level views public class SearchEvents : IReturn { //Optional resultset filters, eg ?Category=Tech&Query=servicestack public string Category { get; set; } public string Query { get; set; } } [Route("/events", "POST")] public class CreateEvent : IReturn { public string Name { get; set; } public DateTime StartDate { get; set; } } [Route("/events/{Id}", "GET")] [Route("/events/code/{EventCode}", "GET")] //*Optional public class GetEvent : IReturn { public int Id { get; set; } public string EventCode { get; set; } //Alternative way to fetch an Event } [Route("/events/{Id}", "PUT")] public class UpdateEvent : IReturn { public int Id { get; set; } public string Name { get; set; } public DateTime StartDate { get; set; } } 

Y sigue un patrón similar para las reseñas del Evento

 [Route("/events/{EventId}/reviews", "GET")] public class GetEventReviews : IReturn { public int EventId { get; set; } } [Route("/events/{EventId}/reviews/{Id}", "GET")] public class GetEventReview : IReturn { public int EventId { get; set; } public int Id { get; set; } } [Route("/events/{EventId}/reviews", "POST")] public class CreateEventReview : IReturn { public int EventId { get; set; } public string Comments { get; set; } } 

La implementación debería ser bastante directa en función de estos mensajes, que (dependiendo del tamaño de la base de código) organizaría en 2 clases de servicio de eventos y clases de eventos de eventos . Debo señalar que utilizo la pluralización para los nombres de solicitud de servicio DTO para evitar chocar con los modelos de datos del mismo nombre.

Aunque he separado UpdateEvent y CreateEvent aquí, a veces los fusionaré en una única operación StoreEvent idempotente si el caso de uso lo permite.

Estructura del proyecto físico

Idealmente, el proyecto AppHost de nivel raíz debe mantenerse ligero y sin implementación. Aunque para proyectos pequeños con solo unos pocos servicios, está bien que todo esté en un solo proyecto y simplemente hacer crecer su architecture cuando sea necesario.

Para proyectos medianos y grandes, recomendamos la estructura física debajo de la cual, para los fines de este ejemplo, asumiremos que nuestra Aplicación se llama EventMan .

El orden de los proyectos también muestra sus dependencias, por ejemplo, el proyecto de nivel superior EventMan referencia a todos los subproyectos, mientras que el último proyecto de EventMan.ServiceModel no hace referencia a ninguno :

 - EventMan AppHost.cs // ServiceStack ASP.NET Web or Console Host Project - EventMan.ServiceInterface // Service implementations (akin to MVC Controllers) EventsService.cs EventsReviewsService.cs - EventMan.Logic //For larger projs: pure C# logic, data models, etc IGoogleCalendarGateway //Eg of a external dependency this project could use - EventMan.ServiceModel //Service Request/Response DTOs and DTO types Events.cs //SearchEvents, CreateEvent, GetEvent DTOs EventReviews.cs //GetEventReviews, CreateEventReview Types/ Event.cs //Event type EventReview.cs //EventReview type 

Con los EventMan.ServiceModel DTO guardados en su propia implementación independiente y dll libre de dependencia, puede compartir libremente este dll en cualquier proyecto de cliente .NET tal cual, que puede usar con cualquiera de los Clientes de servicio C # generics para proporcionar una API de tipeo de extremo a extremo sin código genérico.


Actualizar

  • Esta estructura de proyecto recomendada ahora está contenida en todas las plantillas VS.NET de ServiceStackVS .

  • El Ejemplo REST de cliente simple tiene un pequeño ejemplo autónomo del mundo real de crear un servicio REST simple que utiliza un RDBMS.

No estoy seguro si esto ayudará en su escenario / entendimiento, pero encuentro útil esta presentación:

Diseñando una bella API REST + JSON