Dans le exemples pour ServiceStack, je ne vois pas une seule application qui est le site Web ASP.NET MVC en premier, puis le service ServiceStack en second.
Prenons une application Web ASP.NET MVC très simple qui rend les produits via les vues. Il utilise des contrôleurs, des vues, des modèles et des modèles de vue.
Disons que nous avons un modèle de Product
qui est conservé dans une base de données de document. Supposons que nous ayons un modèle de vue de ProductViewModel
qui est mappé à partir de Product
et affiché dans MVC Razor View/PartialView.
c'est donc un côté Web des choses .. supposons maintenant que nous voulons ajouter un service renvoyant des produits à divers clients comme les applications Windows 8.
Les classes de demande/réponse doivent-elles être complètement déconnectées de ce que nous avons déjà? Notre ProductViewModel
peut déjà contenir tout ce que nous voulons retourner du service.
Comme nous avons déjà Product
(classe de modèle), nous ne pouvons pas avoir une autre classe Product
dans l'espace de noms de l'API ... nous le pourrions, mais cela rend les choses floues et j'aimerais éviter cela.
Alors, devrions-nous introduire la classe ProductRequest
et la classe ProductRequestResponse
(hérite ProductViewModel) autonomes dans l'espace de noms de l'API?
Ainsi ProductRequestResponse : ProductViewModel
?
Ce que je dis, c'est que nous avons déjà les classes Model et ViewModel et pour construire des classes Request et Response pour le service SS, nous aurions à créer deux autres fichiers, principalement en copiant tout à partir des classes que nous avons déjà. Cela ne semble pas DRY pour moi, cela pourrait suivre les directives de séparation des préoccupations mais DRY est important aussi, en fait plus que de tout séparer (tout séparer conduit à la duplication de code).
Ce que j'aimerais voir, c'est un cas où une application Web a déjà été créée, elle comporte actuellement des modèles et des modèles de vue et renvoie les vues appropriées pour l'affichage sur le Web, mais peut être étendue en un service entièrement fonctionnel pour prendre en charge les clients programmatiques. Comme AJAX clients etc ... avec ce que nous avons déjà.
Autre chose:
Si vous regardez cet exemple https://github.com/ServiceStack/ServiceStack.Examples/blob/master/src/ServiceStack.MovieRest/MovieService.cs
vous verrez qu'il y a Movie
classe de demande et Movies
classe de demande (une pour une seule demande de film, l'autre pour une liste de films). À ce titre, il existe également deux services, MovieService
et MoviesService
, l'un traitant des demandes pour un seul film, l'autre pour un genre de films.
Maintenant, même si j'aime l'approche SS des services et je pense que c'est la bonne, je n'aime pas ce genre de séparation simplement à cause du type de demande. Et si je voulais des films de réalisateur? Serais-je en train d'inventer une autre classe de requêtes ayant une propriété Director
et encore un autre service (MoviesByDirector
) pour elle?
Je pense que les échantillons devraient être orientés vers un seul service. Tout ce qui concerne les films doit être sous un même toit. Comment y parvenir avec ServiceStack?
public class ProductsService : Service
{
private readonly IDocumentSession _session;
private readonly ProductsHelperService _productsHelperService;
private readonly ProductCategorizationHelperService _productCategorization;
public class ProductRequest : IReturn<ProductRequestResponse>
{
public int Id { get; set; }
}
// Does this make sense?
// Please note, we use ProductViewModel in our Views and it holds everything we'd want in service response also
public class ProductRequestResponse : ProductViewModel
{
}
public ProductRequestResponse GetProducts(ProductRequest request)
{
ProductRequestResponse response = null;
if (request.Id >= 0)
{
var product = _session.Load<Product>(request.Id);
response.InjectFrom(product);
}
return response;
}
}
L'interface la plus importante que vous puissiez créer dans l'ensemble de votre système est votre contrat de service externe, c'est à cela que les consommateurs de votre service ou application se lieront, c'est-à-dire les sites d'appel existants qui souvent ne seront pas mis à jour avec votre code -base - tous les autres modèles sont secondaires.
À la suite de recommandation de Martin Fowler pour l'utilisation des DTO (Objets de transfert de données) pour les services distants ( MSDN ), ServiceStack encourage l'utilisation de POCO propres et non contaminés pour définir un contrat bien défini avec qui devrait être conservé dans un fichier .dll largement sans implémentation et sans dépendance. Les avantages de cela vous permettent de réutiliser les DTO typés utilisés pour définir vos services avec, tels quels, dans vos clients C # /. NET - fournissant une API typée de bout en bout sans l'utilisation de code-gen ou d'autres machines artificielles.
Garder les choses DRY ne doit pas être confondu avec une déclaration claire d'intention, ce que vous devez éviter d'essayer de DRY ou cacher derrière l'héritage , propriétés magiques ou tout autre mécanisme. Le fait d'avoir des DTO propres et bien définis fournit une source de référence unique que tout le monde peut consulter pour voir ce que chaque service accepte et renvoie, il permet à vos développeurs clients et serveurs de commencer leur travail immédiatement et de se lier à les modèles de services externes sans que l'implémentation n'ait été écrite.
Garder les DTO séparés vous donne également la liberté de re-factoriser l'implémentation de l'intérieur sans casser les clients externes, c'est-à-dire que votre service commence à mettre en cache les réponses ou exploite une solution NoSQL pour remplir vos réponses.
Il fournit également la source faisant autorité (qui n'est pas divulguée ou couplée dans la logique de votre application) qui est utilisée pour créer les pages de métadonnées générées automatiquement, des exemples de réponses, la prise en charge de Swagger, les XSD, les WSDL, etc.
Bien que nous encouragions à conserver des modèles DTO séparés, vous n'avez pas besoin de maintenir votre propre mappage manuel car vous pouvez utiliser un mappeur comme AutoMapper ou utiliser le support de mappage automatique intégré de ServiceStack, par exemple:
Créez une nouvelle instance DTO, remplie de propriétés correspondantes sur viewModel:
var dto = viewModel.ConvertTo<MyDto>();
Initialisez DTO et remplissez-le avec les propriétés correspondantes sur un modèle de vue:
var dto = new MyDto { A = 1, B = 2 }.PopulateWith(viewModel);
Initialisez DTO et remplissez-le avec non-default propriétés correspondantes sur un modèle de vue:
var dto = new MyDto { A = 1, B = 2 }.PopulateWithNonDefaultValues(viewModel);
Initialisez DTO et remplissez-le avec les propriétés correspondantes qui sont annotées avec l'attribut Attr sur un modèle de vue:
var dto = new MyDto { A=1 }.PopulateFromPropertiesWithAttribute<Attr>(viewModel);
Lorsque la logique de mappage devient plus compliquée, nous aimons utiliser des méthodes d'extension pour conserver le code DRY et maintenir le mappage à un endroit facilement consommable depuis votre application, par exemple:
public static class MappingExtensions
{
public static MyDto ToDto(this MyViewModel viewModel)
{
var dto = viewModel.ConvertTo<MyDto>();
dto.Items = viewModel.Items.ConvertAll(x => x.ToDto());
dto.CalculatedProperty = Calculate(viewModel.Seed);
return dto;
}
}
Qui est maintenant facilement consommable avec juste:
var dto = viewModel.ToDto();
Si vous n'êtes pas lié spécifiquement à ServiceStack et que vous souhaitez simplement "un service entièrement fonctionnel pour prendre en charge les clients programmatiques ... avec ce que nous avons déjà", vous pouvez essayer ce qui suit: Demandez à vos contrôleurs de renvoyer un ViewResult
ou un JsonResult
basé sur l'en-tête d'acceptation de la demande - Request.AcceptTypes.Contains("text/html")
ou Request.AcceptTypes.Contains("application/json")
.
ViewResult
et JsonResult
sont ActionResult
, donc la signature des actions reste la même, et View()
et Json()
acceptent un ViewModel. De plus, si vous avez un ControllerBase, vous pouvez créer une méthode de base (par exemple protected ActionResult RespondWith(Object viewModel)
) qui appelle View () ou Json () de sorte que la modification du code existant est minime.
Bien sûr, si vos ViewModels ne sont pas purs (c'est-à-dire qu'ils contiennent des éléments spécifiques au HTML ou si vous comptez sur la magie de ViewBag), c'est un peu plus de travail. Et vous n'obtiendrez pas SOAP ou d'autres types de liaison fournis par ServiceStack, mais si votre objectif est de prendre en charge une interface de données JSON avec un minimum de modifications de code dans l'application MVC existante, cela pourrait être une solution .
Lp