C#: Как правильно организовать сервисы в Business Logic Layer с помощью DI?

Добрый день!

Прошу подсказать, как грамотно организовать взаимодействие сервисов.
Допустим, для примера, есть следующая структура сервисов:

public abstract class BaseService : IService
{
	protected readonly IUnitOfWork _uow;
	
	public BaseService(IUnitOfWork uow)
	{
		_uow = uow; 
	}
	
	// Other base protected & private methods
}


public class FooService : BaseService, IFooService
{
	public FooService(IUnitOfWork uow)  : base(uow) { }
	
	public void CreateFoo(FooDTO foo) 
	{
		// Вопрос как получить репозиторий для сущности Foo ?
		IRepository<Foo> rep = _uow.Getrepository<Foo>();
		// Но таким образом я получу, только Generic репозиторий, 
		// как получить "кастомный" репозиторий, например, IFooRepository?
				
		// Insert Foo entity
		_uow.Commit();
	}
}


public class BarService : BaseService, IBarService
{
	private readonly IFooService _fooService;
	
	public BarService(IUnitOfWork uow, IFooService fooService)  : base(uow) 
	{ 
		_fooService = fooService;
	}
	
	public void CreateBar(BarDTO bar) 
	{
		// Insert Bar entity
		
		// После чего необходимо вызвать FooService.CreateFoo(fooDTO)
		_fooService.CreateFoo(new FooDTO()); // только для примера создается пустой FooDTO-объект
		// При этом действие должно выполниться одной транзакцией
		// т.е.  полчается, что _uow.Commit() в методе FooService.CreateFoo не должен сработать,
		// а должен сработать _uow.Commit для всех сущностей (и Bar и Foo) тот что ниже??
			
		_uow.Commit();
	}
}


Вопросы:
1) Как из _uow получить "кастомный" репозиторий, например IFooRepository или IBarRepository? Если подключить DI в конструкторе,то я не совсем понимаю, ведь эти репозитории должны быть взяты из указанного первым параметром uow?
2) Как организовать транзакцию для BarService.CreateBar(...) ?

Благодарен за любую помощь!
  • Вопрос задан
  • 1204 просмотра
Пригласить эксперта
Ответы на вопрос 2
Valeriy1991
@Valeriy1991
Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
1. BarService как бизнес-сервис не должен знать о другом бизнес-сервисе, т.е. о FooService. Если приходит понимание, что в BarService нужно вызывать метод из FooService, значит Ваш FooService должен стать компонентом более низкого уровня, чем бизнес-сервис. Например, FooManager (вспомните всякие ***Manager из ASP.NET Identity). Тогда этот FooManager можно будет использовать не только в BarService, но в каком-нибудь другом ***Service. Архитектурно будет выглядеть так: наверху ***Service (бизнес-логика), ниже - ***Manager (тоже бизнес-логика), еще ниже - ***Repository (а это уже DAL).
2. Да, бывает необходимость расширить Generic-репозиторий конкретным репозиторием. Но тут надо очень хорошо подумать, почему мы это делаем. Возможно, мы пытаемся на конкретный репозиторий навесить часть бизнес-логики? Если так, то это уже не конкретный репозиторий и даже не компонент уровня DAL - это компонент уровня бизнес-логики. Ну да ладно, допустим, мы поняли, что нам-таки нужен IFooRepository. Я сталкивался с таким решением этой задачи: в IUnitOfWork добавляется новое свойство:
public IFooRepository FooRepository {get; }
И все. Инициализация выполняется внутри экземпляра UnitOfWork, например, так:
public IFooRepository FooRepository {get; } = new FooRepository();

а "наружу" нам доступен FooRepository:
_uow.FooRepository.CallSomeMethod(...);
В итоге в BarService Ваш код будет выглядеть примерно так:
public void CreateBar(BarDTO bar) 
{
    // Insert Bar entity
    
    _uow.FooRepository.CreateFoo(new FooDTO()); 

    _uow.Commit();
}


Но повторюсь. В Вашем примере у меня есть подозрения, что метод CreateFoo должен принадлежать не FooRepository, а какому-то компоненту уровня бизнес-логики (но в иерархии он не является бизнес-сервисом). Например, FooManager или FooCreator какой-нибудь (который отвечает только за создание объекта). Помните принцип SRP.
Ответ написан
Комментировать
@heartdevil
плыву как воздушный шарик
Как и uof инжектите
BarService(IFooRepository fooRepository, IUnitOfWork uow)


Чтобы одной транзакцией сохранилось, просто не вызывайте _uow.Commit() для CreateFoo, а вызовите его после того, как сделаете все манипуляции с сущностями. Этого можно добиться, передав необязательный параметр в метод CreateFoo, для указания сохранения или не сохранения.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы