Это правильный способ создания объекта с зависимостями из модели предметной области? - PullRequest
2 голосов
/ 14 сентября 2010

Я пытаюсь избежать использования анемичной модели предметной области, поэтому я стараюсь сохранить как можно больше логики в самой модели предметной области. У меня есть метод с именем AddIngredient, который должен добавить новый KeyedObject к моему Recipe агрегату.

Поскольку предполагается, что сами доменные модели лишены репозиториев, я получаю ингредиент с помощью бизнес-правила класс:

public class Recipe : AggregateObject
{
    public void AddIngredient(int ingId, double quantity)
    {
        GetIngredientMessage message = new GetIngredientMessage();
        message.IngredientId = ingId;

        GetIngredient handler = ServiceLocator.Factory.Resolve<GetIngredient>();
        Ingredient ingredient = handler.Execute(message);

        Ingredients.Add(new OriginalIngredient()
        {
            Ingredient = ingredient,
            Quantity = quantity
        });
    }
}

Как видите, я использую строку строка ServiceLocator.Factory.Resolve<GetIngredient>();, чтобы получить класс GetIngredient моего бизнес-правила. GetIngredient - это простой обработчик команд, который выглядит следующим образом:

public class GetIngredient : ICommandHandler<Ingredient, GetIngredientMessage>
{
    private readonly IIngredientRepository _ingredientRepository;

    public GetIngredient(IIngredientRepository ingredientRepository)
    {
        _ingredientRepository = ingredientRepository;
    }
}

Я присваиваю свой класс фабрики IoC ServiceLocator.Factory, поэтому Домен может использовать свои собственные интерфейсы, не видя конкретную реализацию класса:

 ServiceLocator.Factory = new IoCFactory();

Я почти уверен, что делаю что-то не так, потому что все это выглядит немного странно.

  • Может кто-нибудь заметить что-то явно не так?
  • Есть ли более подходящий способ создания обработчика бизнес-правила, такого как GetIngredient, без статической ссылки на мой IoC Factory?

1 Ответ

3 голосов
/ 15 сентября 2010

Предлагаю вам ввести еще один слой в дизайн - прикладной уровень. Эта ответственность за уровень будет заключаться в переводе команд (либо явно инкапсулированных в объекты команд, либо неявно передаваемых как int ingId, double quantity) в вызовы модели домена (Recipe.AddIngredient).

Тем самым вы переместите ответственность за поиск ингредиента по его идентификатору на слой над доменом, где вы сможете безопасно использовать репозитории напрямую, не вводя нежелательную связь. Преобразованное решение будет выглядеть примерно так:

public class ApplicationLayer
{
   private readonly IRecipeRepository _recipeRepository;
   private readonly IIngredientRepository _ingredientRepository;

   /*
    * This would be called by IoC container when resolving Application layer class.
    * Repositories would be injected by interfacy so there would be no coupling to
    * concrete classes.
    */
   public ApplicationLayer(IRecipeRepository recipeRepository, IIngredientRepository ingredientRepository)
   {
      _recipeRepository = recipeRepository;
      _ingredientRepository = ingredientRepository;
   }

   public void AddIngredient(int recipeId, int ingId, double quantity)
   {
       var recipe = _recipeRepository.FindById(recipeId);
       var ingredient = _ingredientRepository.FindById(ingId);
       recipe.AddIngredient(ingredient, quantity);   
   }
}

А теперь упрощенный класс Recipe будет выглядеть примерно так:

public class Recipe : AggregateObject
{
    public void AddIngredient(Ingredient ingredient, double quantity)
    {
        Ingredients.Add(new OriginalIngredient()
        {
            Ingredient = ingredient,
            Quantity = quantity
        });
    }
}

Надеюсь, это поможет.

...