Имея неактуальные дополнительные параметры и DTO для имплементации IRepository в доменном слое - PullRequest
1 голос
/ 19 июня 2019

У меня есть решение, которое я реализовал с помощью архитектуры Onion и DDD, например eShopOnContainers .

Но у меня есть проблема для этого, поэтому я решил поделиться ею с вами.Я пытаюсь объяснить это на примере.Вы предполагаете, что у меня есть interface с именем IOrderRepository как IOrderRepository

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

Первая проблема

Я реализовал Add методкак OrderRepository , но мне нужны дополнительные параметры в методе Add, например, следующий код:

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             
    }

Как видите, реализация IOrderRepository неправильна из-за дополнительных параметров, которые мне нужны.

Неправильные решения для первого выпуска

У меня есть два неправильных решения для решения этой проблемы.

1- Регулировка IOrderRepository

Изменение входных IOrderRepository параметров путем добавления параметров, таких как:

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

Как я знаю, для par1 and par2 нет бизнес-правила и реализации DDDПрежде всего, я должен указать IRepository, но, используя это решение, я помещаю проблемы уровня инфраструктуры в слой домена, который является неправильной архитектурой.

2 - перевод IOrderRepository на уровень инфраструктуры

Я могу поставить IOrderRepository в слое инфраструктуры, но это еще одна неправильная дугаархитектура, потому что, насколько я знаю, интерфейсы такого рода должны быть расположены в Domain layer.

Мой первый вопрос

1 - Как я могу использовать дополнительныепараметры в методах Репозитория на уровне инфраструктуры для реализации IRepository уровня домена, что нет никакой связи между параметрами и уровнем домена?

Второй выпуск

Как вы можете видеть в IOrderRepository, я должен реализовать метод GetAsync, который возвращает OrderDTO, включая Order и дополнительные параметры.Как я знаю, я не могу использовать DTO (Data Transfer Object) в доменном слое.Я не мог придумать идею справиться с этим.

Мой второй вопрос

2 - Как я могу вернуть методы DTO OrderRepository, которыена уровне инфраструктуры, но я не могу применить его в IOrderRepository, который находится на уровне домена.

Заранее спасибо за ваше время.

Ответы [ 3 ]

4 голосов
/ 19 июня 2019

Поскольку у нас нет контекста относительно того, зачем вам нужны эти два параметра, вы также можете использовать две другие, возможно, менее навязчивые опции: -

Во-первых, у вас может быть новый класс, который наследуется от Order, а также содержит следующие дополнительные параметры:

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

В вашем хранилище вы можете затем разыграть или выполнить безопасное приведение к классу MyOrder.

Другой вариант - использовать метод расширения для IOrderRepository:

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

Возможно, они не будут работать в вашем сценарии, поэтому YMMV. В любом случае вам может понадобиться еще раз взглянуть на ваш дизайн.

1 голос
/ 25 июня 2019

Это похоже на проблему X-Y, в вашем коде есть несколько проблем, и вы недостаточно хорошо объясняете проблему, просто объясняете, что вы хотите в качестве ответа.

Если вы посмотрите на код, с которым вы связаны, то это их реализация IRepository (я опускаю нерелевантные методы для ответа на вопрос)

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

Здесь вы можете видеть, что они очищены только для Ордена, а не для DTO. Однако в предоставленной вами реализации вы возвращаете DTO, который невозможно было бы вернуть без ссылки на отдельный параметр

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

Возможно, вам нужна более сложная архитектура, но не будет достаточно места для размещения полного кода, поэтому я просто дам вам Gyst Примите оба параметра Order и OrderDTO и добавьте инфраструктуру для обработки адаптаций

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);
}

Тогда вам понадобится базовый класс для обработки операций

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);
     }
}

Теперь ваш базовый репозиторий обрабатывает большую часть вашей базовой логики, все, что вам нужно сделать, это реализовать адаптер, например, Automapper - хороший способ.

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
}

Вы должны быть в состоянии получить Gyst от этого

0 голосов
/ 22 июня 2019

Что в параметрах?

Если это часть Ордена, ваша модель должна это отражать. Принятие предположительно несвязанной информации обычно является очень четким признаком того, что ваша объектная модель не отражает реальность. В этом весь смысл DDD.

Если это другие вещи, которые будут согласованы на протяжении всего срока службы хранилища, передайте их в качестве параметров хранилищу при его построении (внедрение зависимостей). Затем он может использовать их для хранения предмета.

Вот пример последнего - скажем, у меня есть компания электронной коммерции, и у нас есть устаревшая система заказов, которую нужно уведомлять о старых заказах, но в новых заказах она больше не нужна. Я могу инкапсулировать это поведение в моей фабрике, как (пример 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
     }
}
...