Как обрабатывать единицу работы с внедрением зависимостей и репозиториями

Я унаследовал некоторый код с UnitOfWorkFactory, который создает единицу работы внутри каждого метода репозитория. Проблема в том, что один метод репозитория редко является полной единицей работы, поэтому, если что-то пойдет не так, скажем, OrderService.PlaceOrder, он не может просто откатить/отбросить эту единицу работы, поскольку это не одна единица.

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

Я мог бы создать единицу работы и внедрить в нее конструкторы сервисов, репозиториев и презентаторов, но тогда она осталась бы за рамками одной единицы работы. Это настольное приложение, поэтому презентатор и любые службы, которые он передает, могут быть активны для нескольких единиц работы.

Единственный способ, которым я могу передать единицу работы, - это добавить ее в качестве параметра ко всем методам службы/репозитория. Я не могу не думать, что должен быть лучший способ, чем этот, я что-то упустил?

Код выглядит примерно так:

Репозиторий:

class OrderRepository
{
    public UnitOfWorkFactory UnitOfWorkFactory;

    public OrderRepository(UnitOfWorkFactory unitOfWorkFactory)
    {
        UnitOfWorkFactory = unitOfWorkFactory;
    }

    public void Save(Order order)
    {
        using(var uow = UnitOfWorkFactory.Create())
        {
            // save order
            uow.commit();
        }
    }
}

Сервис:

class OrderService
{
    protected IOrderRepository OrderRepository;
    protected IProductService ProductService;

    public OrderService(IOrderRepository orderRepository, IProductRepository productService)
    {
        OrderRepository = orderRepository;
        ProductService = productService;
    }

    public void PlaceOrder(Order order)
    {
        foreach(var item in order.Items)
        {
            if(!ProductService.IsInstock(item.Product, item.Quantity))
                throw new ProductOutOfStockException(product);

            ProductService.MarkForDispatch(item.Product, item.Quantity);
        }

        OrderRepository.Save(order);
    }

    public void CancelOrder(Order order)
    {
        ProductService.UnmarkForDispatch(item.Product, item.Quantity);

        order.IsCanceled = true;
        OrderRepository.Save(order);
    }
}

Ведущий:

class OrderPresenter
{
    protected IOrderView OrderView;
    protected IOrderService OrderService;

    public OrderPresenter(IOrderView orderView, IOrderService orderService)
    {
        OrderView = orderView;
        OrderService = orderService;
    }

    public void PlaceOrder()
    {
        OrderService.PlaceOrder(order);
    }

    public void CanelOrder()
    {
        OrderService.CancelOrder(order);
    }
}

person Sam    schedule 25.02.2013    source источник
comment
Используете ли вы толстые клиенты или тонкие (например, HTTP)?   -  person jgauffin    schedule 26.02.2013
comment
@jgauffin Это толстый клиент. Это настольное приложение, которое локально обрабатывает все бизнес-правила.   -  person Sam    schedule 26.02.2013


Ответы (2)


У меня нет короткого ответа на этот вопрос. Вы можете проверить полный ответ в этом сообщении в блоге, которое я написал ранее на эту тему. [Еще одна статья из репозитория UoW] .
(Пожалуйста, дайте мне знать, что вы думаете, если прочитали статью)

person Ibrahim Najjar    schedule 26.02.2013
comment
Это выглядит как хорошее решение. В итоге я сделал это немного по-другому (см. Ниже), но я отмечу это как ответ, так как это также решит проблему. - person Sam; 03.03.2013

В конце концов я решил использовать фабрики и передать эту единицу работы. Затем фабрика создает все остальное, используя переданный UOW.

Вот что, если в итоге получилось:

Репозиторий:

public class Repository : IRepoository
{
    IUnitOfWork UnitOfWork;

    public Repository(IUnitOfWork uow)
    {
        UnitOfWork = uow;
    }

    public void SomeOtherMethod(int id)
    {
        // Do something
    }
}

Услуга:

public class Service : IService
{
    IUnitOfWork UnitOfWork;
    IRepoository Repository;

    public Service(IUnitOfWork uow, IRepoository repository)
    {
        UnitOfWork = uow;
        Repository = repository;
    }

    public void SomeMethod(int id)
    {
        Repository.SomeOtherMethod(id);
        // do other stuff
    }
}

Заводы:

public class IUnitOfWorkFactory
{
    IUnitOfWork Create();
}

public class IServiceFactory
{
    IService Create(IUnitOfWork uow);
}

Метод IServiceFactory.Create создает как службу, так и репозиторий, от которого зависит служба, и передает им переданную единицу работы.

Предустановщик:

public class Presenter
{
    protected UnitOfWorkFactory UnitOfWorkFactory;
    protected IServiceFactory ServiceFactory;

    public Presenter(IUnitOfWorkFactory unitOfWorkFactory, IServiceFactory serviceFactory)
    {
        UnitOfWorkFactory = unitOfWorkFactory;
        ServiceFactory = serviceFactory;
    }

    public void DoSomething()
    {
        // Get a new UOW from the factory
        using(var uow = unitOfWorkFactory.Create())
        {
            // Then create the service and repository with the new UOW via the ServiceFactory
            var service = serviceFactory.Create(uow);
            service.SomeMethod(20);

            uow.Commit();
        }
    }
}
person Sam    schedule 02.03.2013
comment
Это также выглядит хорошим решением, но тогда вам понадобится фабрика для каждой службы. если я в конечном итоге добавлю некоторые из ваших идей в свой пост, я не забуду отдать вам должное. - person Ibrahim Najjar; 03.03.2013
comment
Да, это обратная сторона такого подхода. Для меня это не проблема, поскольку код использует библиотеку для внедрения зависимостей, поэтому я могу создать общий фабричный интерфейс, который принимает UOW, и он автоматически создаст реализацию. Вы также можете вручную создать универсальную фабрику, чтобы сделать это так же легко. - person Sam; 03.03.2013