Nous construisons une application Web utilisant AngularJS, C #, l'API Web ASP.Net et Fluent NHibernate .. Nous avons décidé d'utiliser des DTO pour transférer des données vers la couche de présentation (vues angulaires) .. J'ai quelques doutes quant à la structuration générale et la dénomination des DTO . Voici un exemple pour illustrer mon scénario . Disons que j'ai une entité de domaine appelée Client qui ressemble à:
public class Customer
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Address Address { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
}
Maintenant, dans ma couche de vues/présentation, je dois récupérer différentes versions du client, telles que:
1) Identifiant et nom 2) Identifiant, nom et adresse 3) Identifiant, nom, adresse et comptes
J'ai créé un ensemble de DTO pour accomplir ceci:
public class CustomerEntry
{
public int Id { get; set; }
public string Name { get; set; }
}
public class CustomerWithAddress : CustomerEntry
{
public AddressDetails Address { get; set; }
}
public class CustomerWithAddressAndAccounts : CustomerWithAddress
{
public ICollection<AccountDetails> Accounts { get; set; }
}
AddressDetails et AccountDetails sont des DTO qui possèdent toutes les propriétés de leurs entités de domaine correspondantes.
Cela fonctionne très bien pour les requêtes et les extractions de données; la question est qu'est-ce que j'utilise pour les insertions et les mises à jour. Lors de la création d'un nouvel enregistrement client, le nom et l'adresse sont obligatoires et les comptes sont facultatifs. En d'autres termes, j'ai besoin d'un objet avec toutes les propriétés du client. D'où la confusion:
1) Que dois-je utiliser pour les insertions et les mises à jour? Le DTO CustomerWithAddressAndAccounts contient tout, mais son nom semble un peu gênant pour être utilisé pour des insertions/mises à jour.
2) Est-ce que je crée un autre DTO? Si tel est le cas, ne s'agit-il pas d'une duplication, le nouveau DTO ressemblant exactement à CustomerWithAddressAndAccounts?
3) Dernier point, mais non le moindre, la structure de succession DTO décrite ci-dessus semble-t-elle être un bon choix pour l'exigence? Existe-t-il d'autres moyens de modéliser cela?
J'ai lu d'autres articles sur ce sujet mais je ne pouvais pas avancer beaucoup. Une des choses que j'ai choisies de faire était d'éviter d'utiliser le suffixe "DTO" dans les noms de classe ... Je pense que c'est un peu superflu .
Aimerais entendre vos pensées
Merci
La recommandation est que vous ne devriez avoir qu’une classe DTO pour chaque entité suffixé de DTO par exemple. CustomerEntryDTO
pour la Customer
entity
(mais vous pouvez certainement utiliser des hiérarchies d'héritage selon le choix et les exigences).
De plus, ajoutez un type abstrait DTOBase
de classe de base ou une interface; et n'utilisez pas de telles hiérarchies d'héritage profond pour chaque adresse, compte et autres propriétés à inclure dans des objets DTO enfants. Incluez plutôt ces propriétés dans la même classe CustomerEntryDTO
(si possible) ci-dessous:
[Serializable]
public class CustomerEntryDTO : DTOBase, IAddressDetails, IAccountDetails
{
public int Id { get; set; }
public string Name { get; set; }
public AddressDetails Address { get; set; } //Can remain null for some Customers
public ICollection<AccountDetails> Accounts { get; set; } //Can remain null for some Customemer
}
De plus, votre DTO devrait être sérialisable pour pouvoir être passé à travers les limites du processus.
Pour plus sur le modèle DTO, reportez-vous aux articles ci-dessous:
Edit: Au cas où vous ne voudriez pas envoyer certaines propriétés par fil (je sais que vous en auriez besoin de manière conditionnelle, vous auriez besoin d'en explorer davantage), vous pouvez les exclure du Mécanisme de sérialisation utilisant des attributs tels que NonSerialized
(mais cela ne fonctionne que sur les champs et non sur les propriétés, voir l'article de la solution de contournement à utiliser avec les propriétés: Non sérialisé sur la propriété ). Vous pouvez également créer votre propre attribut personnalisé, tel que ExcludeFromSerializationAttribute
, et l'appliquer à des propriétés que vous ne souhaitez pas envoyer chaque fois par fil, en fonction de certaines règles/conditions. Voir aussi:Sérialisation xml conditionnelle
Edit 2: Utilisez des interfaces pour séparer les différentes propriétés de la classe CustomerEntryDTO
. Voir le principe de séparation des interfaces sur Google ou MSDN. Je vais essayer de mettre un exemple d'explication plus tard.
Que dois-je utiliser pour les insertions et les mises à jour?
Les opérations de service sont généralement définies en relation très étroite avec les opérations commerciales. La langue des affaires ne parle pas d’insert et de mise à jour, pas plus que les services.
Le service de gestion client aura probablement une opération Register
qui prend le nom du client et peut-être d'autres paramètres facultatifs.
Est-ce que je crée un autre DTO?
Oui, vous devriez créer un autre DTO.
Parfois, le contrat d’exploitation du service peut être suffisant et il n’est pas nécessaire de définir un DTO distinct pour une opération particulière:
function Register(UserName as String, Address as Maybe(of String)) as Response
Mais la plupart du temps, il est préférable de définir une classe DTO séparée même pour une seule opération de service:
class RegisterCommand
public UserName as String
public Address as Maybe(of String)
end class
function Register(Command as RegisterCommand) as Response
RegisterCommand
DTO peut ressembler beaucoup à CustomerWithAddress
DTO car il a les mêmes champs, mais en réalité, ces 2 DTO ont des significations très différentes et ne se substituent pas.
Par exemple, CustomerWithAddress
contient AddressDetails
, alors qu'une simple représentation d'adresse String
peut suffire à inscrire un client.
L'utilisation d'un DTO distinct pour chaque opération de service prend plus de temps à écrire mais est plus facile à gérer.