1
votes

Avoir des paramètres supplémentaires non pertinents et DTO pour implémenter le référentiel dans la couche de domaine

J'ai une solution que j'ai implémentée par l'architecture Onion et DDD comme eShopOnContainers .

Mais j'ai un problème pour le faire, j'ai donc décidé de le partager avec vous. J'essaye de l'expliquer par un exemple. Vous supposez que j'ai une interface nommée IOrderRepository comme IOrderRepository

public interface IOrderRepository : IRepository<Order>
    {
        Order Add(Order order, int par1, int par2);           
    }

Premier numéro strong >

J'ai implémenté la méthode Add comme OrderRepository mais j'ai besoin de paramètres supplémentaires dans la méthode Add comme le code suivant:

public Order Add(Order order, int par1, int par2)
    {
        // I need a DTO to persist order  
        OrderDTO orderDTO = new OrderDTO({Order = order, Par1 = par1, Par2 = par2 });

        // Call a method with orderDTO parameter as a service to insert order             
    }


11 commentaires

Les méthodes d'extension sont vos amis ici


De plus, votre mélange de types ici ne fera que semer la confusion.


Vous n'avez pas expliqué pourquoi OrderDTO a été introduit


@Nkosi J'ai pensé aux méthodes statiques comme les extensions, mais je l'utilise, comment puis-je connecter la couche de domaine et l'infrastructure? Aussi, je pourrais comprendre ce que vous pensez de la confusion.


@Nkosi Je devrais appeler un service pour obtenir Order qui renvoie OrderDTO .


Vous devrez peut-être mieux expliquer ce que vous essayez de faire. L'exemple actuel est très large et un peu flou. Essayez de restreindre la portée du problème.


@AliSoltani pouvez-vous expliquer pourquoi vous avez besoin de ces champs que plusieurs personnes ont posé cette question, mais vous semblez l'éviter à chaque fois.


@AliSoltani Pourquoi construisez-vous un DTO dans votre méthode d'ajout? Cela sent vraiment un problème X, Y, je suppose que vous n'avez pas réellement besoin de ces champs dans le référentiel


@ johnny5 En fait, je devrais appeler des procédures stockées telles que AddOrder et GetOrder pour insérer Order et obtenir mes informations pour l'utiliser dans l'interface utilisateur. Dans AddOrder , j'ai besoin de paramètres supplémentaires que j'ai mentionnés et en appelant GetOrder j'obtiens OrderDTO .


@AliSoltani Non, vous ne devez pas appeler les procédures stockées. 1. Vous n'implémentez même pas correctement votre interface. 2. vous ne suivez pas correctement le modèle. Voir ma réponse c'est complexe mais c'est un début, pour bien gérer DDD


Je ne comprends pas, avez-vous besoin de passer int par1, int par2 pour enregistrer Order dans le OrderRepository ou pas?


3 Réponses :


4
votes

Puisque nous n'avons pas de contexte pour expliquer pourquoi vous avez besoin de ces deux paramètres, vous pouvez également choisir deux autres options, probablement moins intrusives: -

Premièrement, vous pourriez avoir une nouvelle classe qui hérite de Order et contient également ces paramètres supplémentaires:

public static class SomeExtension
{
    public static void MySave(this IOrderRepository repository, int par1, int par2)
    {
        // do things
    }
}

Dans votre référentiel, vous pouvez ensuite effectuer un cast, ou un cast sécurisé, dans la classe MyOrder .

Une autre option serait d'utiliser une méthode d'extension pour IOrderRepository:

public class MyOrder : Order
{ 
    int Par1 { get; set; }
    int Par2 { get; set; }
}

Peut-être que cela ne fonctionnerait pas dans votre scénario donc YMMV. Dans les deux cas, vous devrez peut-être revoir votre conception.


7 commentaires

Merci pour votre réponse. Votre première suggestion est similaire à appliquer un DTO que je ne peux pas faire à cause de l'utilisation de DDD. Le second m'est impossible car, comme je l'ai mentionné, pour implémenter l'architecture DDD et Onion, je devrais implémenter exactement IOrderRepository et l'utilisation de la méthode statique ne me convient pas.


Puisque nous n'avons pas de contexte, il se peut que ces paramètres supplémentaires ne soient pas liés au domaine. S'ils le sont , vous devez revoir votre conception. Si, cependant, ils sont orthogonaux (tels que la sécurité, les connexions, la journalisation), vous pouvez vous en sortir avec un autre mécanisme en coulisse qui est injecté dans votre référentiel et qui peut fournir les valeurs.


À mon avis, votre réponse est erronée, car tout d'abord je ne peux pas définir une nouvelle classe comme MyOrder dans la couche de domaine. Aussi, je dois implémenter IOrderRepository que je le définis dans la couche Domain, la méthode statique dans ce cas est inutile.


Vous pourriez peut-être avoir un service d'application qui exécute les fonctionnalités supplémentaires que vous auriez faites avec ces paramètres supplémentaires. Le service peut appeler le référentiel et après cela, vous effectuez tout ce dont vous avez besoin. De cette façon, vous n'avez pas à transmettre ces paramètres au référentiel. Encore une fois, sans le contexte approprié, il est difficile de vous aider à trouver une solution.


Tu as raison. Je pourrais faire tout ce dont j'ai besoin dans la couche Application, en combinant des données pour créer OrderDTO par exemple, mais mon problème est OrderRepository que je devrais appeler une procédure stockée pour insérer des données également comme l'obtenir. Pour appeler sp, j'ai besoin de paramètres supplémentaires et l'objet de retour de get sp est OrderDTO . Dans DDD, je dois implémenter IOrderRepository qui ne sait rien des paramètres supplémentaires et des DTO. Jusqu'à présent, je n'ai pas trouvé de solution à ce problème.


Que représentent ces paramètres? S'agit-il d'une infrastructure telle que des dates ou un nom d'utilisateur? Sont-ils communs à tous vos SP?


Les paramètres dans les projets réels peuvent être de type différent, mais ils incluent généralement des dates et des noms d'utilisateur. Mais ils ne sont pas communs à l'ensemble de mes SP. En fait, je devrais parfois appeler des SP qui renvoient des objets qui ne sont pas les mêmes que mes entités.



0
votes

Que contiennent les paramètres?

Si cela fait partie de la commande, votre modèle doit en tenir compte. Le fait de devoir intégrer des informations prétendument sans rapport est généralement un signe très clair que votre modèle d'objet ne reflète pas la réalité. C'est tout l'intérêt de DDD.

Si ce sont d'autres choses qui seront cohérentes tout au long de la vie du référentiel, donnez-les comme paramètres au référentiel lors de sa construction (injection de dépendances). Il peut ensuite les utiliser en interne pour stocker l'article.

Voici un exemple de ce dernier - disons que j'ai une société de commerce électronique et que nous avons un ancien système de commande qui doit être notifié sur les commandes plus anciennes, mais dans les nouvelles commandes, il n'est plus nécessaire. Je peux encapsuler ce comportement dans mon usine comme (exemple C #):

public class OrderRepository : IOrderRepository
{
     private LegacyOrderService _legacyService;
     public OrderRepository(LegacyOrderService legacyService){
          _legacyService = legacyService;
     }

     public Order Add(Order order){
         if(!order.isNewOrder){
              _legacyService.notify(order.id);
         }

         # do the rest
     }
}


2 commentaires

Merci pour votre réponse. Tout d'abord, j'ai mis à jour ma question pour couvrir tous mes problèmes. Veuillez le revoir. Deuxièmement, je trouve une solution pour résoudre mon premier problème comme vous en injectant un ExtraParametersProviderService pour obtenir des paramètres supplémentaires dans l'infrastructure, mais j'ai rencontré avec l'implémentation de la méthode Get .


La question de ces paramètres est l'infrastructure ou les informations de domaine reste cependant. S'ils sont les premiers, ils n'appartiennent pas à votre modèle de domaine, et probablement pas à votre dépôt.



1
votes

Cela ressemble à un problème XY, il y a plusieurs problèmes avec votre code et vous n'expliquez pas assez bien le problème, vous expliquez simplement ce que vous voulez comme réponse.

Si vous regardez le code que vous avez lié à ceci est leur implémentation d'IRepository (j'ai omis des méthodes non pertinentes pour répondre à la question)

public class OrderAdapter : IAdaptable<Order, OrderDTO>
{
    IOtherDependentService _service;
    public OrderAdapter(IOtherDependentService service)
    {
        this._service = service;
    }

    OrderDTO ToDTO(Order order)
    {
       var orderPars = this._service.GetParsFromOrder(order.Id);
       var dto = new OrderDTO{
           Order = order, 
           Pars1 = orderPars.Pars1, 
           Pars2 = orderPars.Pars2
       };

       return dto;

    }

    //.... Implement the rest of the interface you get the picture
}

Ici, vous pouvez voir qu'ils ne fonctionnent que sur la commande et non sur le DTO Pourtant, dans l'implémentation que vous avez fournie, vous renvoyez le DTO, ce qui serait impossible de renvoyer sans lien un paramètre séparé

public class BaseRepository<TEntity, TDTO> : IRepository<TEntity, TDTO>
    where TEntity : class, new()
{
     protected DbContext _context;
     protected IAdaptable<TEntity, TDTO> _adapter;
     public BaseRepository(DbContext context, IAdaptable<TEntity, TDTO> adapter)
     {
         this._context = context;
         this._adapter = adapter;
     }

     public TDTO Add(TDTO dto)
     {
        var entity = this._adapter.ToEntity(dto, new TEntity());
        this._context.Set<TEntity>().Add(entity);
        this._context.SaveChanges();

         return this._adapter.ToDTO(entity);
     }
}

Vous voulez probablement une architecture plus complexe mais il a gagné pas assez d'espace pour mettre en page le code complet donc je vais juste vous donner un Gyst Prenez à la fois les paramètres Order et OrderDTO, et ajoutez une infrastructure pour gérer les adaptations

public interface IOrderRepository : IRepository<OrderDTO>
{
    OrderDTO Add(OrderDTO order); 
    Task<OrderDTO> GetAsync(int orderId);
}


public interface IAdaptable<TEntity, TDTO>
{
    TDTO ToDTO(TEntity entity);
    TEntity ToEntity(TDTO entityDTO, TEntity entity);
}

Ensuite, vous aurez besoin d'une classe de base pour gérer les opérations

public interface IOrderRepository : IRepository<Order>
{
    Order Add(Order order);               
    Task<OrderDTO> GetAsync(int orderId); 
}  


3 commentaires

Merci pour votre temps pour répondre à ma question, tout d'abord, votre code inclut IRepository qui est impossible en raison de l'emplacement de IRepository qui se trouve dans la couche de domaine. Nous devrions donc l'utiliser uniquement pour les entités de domaine. Enfin, à mon avis, mon problème est assez clair, j'ai besoin de plus de paramètres pour classer AddOrder et la valeur de retour de GetOrder sp est OrderDTO , cependant, dans IOrderRepository ne sais rien de ces choses. Comment puis-je mettre en œuvre correctement la couche de domaine et le référentiel.


@AliSoltani, c'est vous qui avez introduit ces problèmes dans votre question, vous créez un dto dans la méthode add. Non, vous n'êtes pas clair, vous dites simplement que vous avez besoin de ces paramètres, mais vous n'expliquez pas pourquoi vous en avez besoin. S'ils ne sont pas pertinents pour la création d'une commande, vous n'en avez pas besoin s'ils sont pertinents pour créer une commande, ils devraient être dans l'ordre. Si ce sont des propriétés droites, vous devez changer l'architecture pour utiliser quelque chose de similaire au mien.


Ali explique clairement pourquoi vous en avez besoin au lieu de simplement dire que vous en avez besoin. Montrez-nous dans le code qu'il est clair que votre architecture est le problème, mais vous essayez de nous demander de créer votre solution au lieu de créer la bonne solution