views:

54

answers:

1

I'm trying to avoid ending up with an anaemic Domain Model, so I'm attempting to keep as much logic as possible within the domain model itself. I have a method called AddIngredient, which needs to add a new KeyedObject to my Recipe Aggregate.

As the Domain Models themselves are meant to be devoid of repositories, I'm getting the ingredient via a business rule class:

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

As you can see, I'm using a line the line ServiceLocator.Factory.Resolve<GetIngredient>(); to obtain my GetIngredient business rule class. GetIngredient is a simple command handler that looks like the following:

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

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

I assign my IoC factory class to the ServiceLocator.Factory, so the Domain has the ability to use its own interfaces, without seeing the concrete class implementation:

 ServiceLocator.Factory = new IoCFactory();

I'm pretty sure I'm doing something wrong as it all feels a little bit bodge-like.

  • Can anyone spot anything blatantly wrong?
  • Is there a more appropriate way to instantiate a business rule handler such as GetIngredient without a static reference to my IoC Factory?
+3  A: 

I suggest you introduce another layer into the design -- the Application layer. This layer responsibility would be to translate commands (either explicitly encapsulated in command objects or passed implicitly as int ingId, double quantity) into domain model invocations (Recipe.AddIngredient).

By doing so you'll move the responsibility of finding an ingredient by its id to a layer above domain, where you can safely make use of repositories directly without introducing unwanted coupling. The transformed solution would look something like this:

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

And the now simplified Recipe class would look something like this:

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

Hope that helps.

Szymon Pobiega
Arrr! Spot on. This was a totally dumb moment. Every where else in my domain model I add an object model, not via an ID. So this answer can basically be summarised by saying "You must add object models to your domain model, not add via an ID". That makes much more sense. Correct?
GenericTypeTea
@GenericTypeTea not always. Sometimes it's enough with Id. But the real issue here what Szymon nicely resolves is infrastructure logic in domain model and quite direct domain model awareness of persistence.
Arnis L.