5

In order not to redo things several times I wanted to factorize common stuff. For Instance, let's say we have a cow and a horse. The cow produces milk, the horse runs fast, but both eat grass.

public class Herbivorous
{
   protected int Quantity;

   public void EatGrass(int quantity)
   {
      var evt= Build.GrassEaten
                    .WithQuantity(quantity);
      RaiseEvent(evt);
   }

   public void Apply(GrassEaten evt)
   {
       _quantity= evt.Quantity;
   }
}

public class Horse : Herbivorous
{
   private bool _HasFastRun;

   public void RunFast()
   {
      var evt= Build.FastRun;
      RaiseEvent(evt);
   }

   public void Apply(FastRunevt)
   {
       _HasFastRun= true;
   }
}

public class Cow: Herbivorous
{
   private bool _IsMilkProduced;

   public void ProduceMilk()
   {
      var evt= Build.MilkProduced;
      RaiseEvent(evt);
   }

   public void Apply(MilkProduced evt)
   {
       _IsMilkProduced= true;
   }
}

To eat Grass, my application receives a command in Json or xml, or whatever that deserialise into this class:

namespace Herbivorous
{
   public class EatGrass : CommandBase
   {
      public Guid IdHerbivorous {get; set;}
      public Guid CommitId {get; set;}
      public long Version {get; set;}
      public int Quantity {get; set;}
   }
}

The command handler should be :

public class EatGrassHandler : CommandHandler<EatGrass>
{
   public override CommandValidation Execute(EatGrass cmd)
   {
      Contract.Requires<ArgumentNullException>(cmd != null);
      Herbivorous herbivorous= EventRepository.GetById<Herbivorous>(cmd.Id);
      if (herbivorous.IsNull())
         throw new AggregateRootInstanceNotFoundException();
      herbivorous.EatGrass(cmd.Quantity);
      EventRepository.Save(herbivorous, cmd.CommitId);
   }
}

so far so good. I get a Herbivorous object , I have access to its EatGrass function, whether it is a horse or a cow doesn't matter really. The only problem is here :

EventRepository.GetById<Herbivorous>(cmd.Id)

Indeed, let's imagine we have a cow that has produced milk during the morning and now wants to eat grass. The EventRepository contains an event MilkProduced, and then come the command EatGrass. With the CommandHandler, we are no longer in the presence of a cow and the herbivorious doesn't know anything about producing milk . what should it do?

Should I have explicit contextual command like :

namespace Herbivorous.Cow
    {
       public class EatGrass : CommandBase
       {
          public Guid IdHerbivorous {get; set;}
          public Guid CommitId {get; set;}
          public long Version {get; set;}
          public int Quantity {get; set;}
       }
       public class ProduceMilk : CommandBase
       {
          public Guid IdHerbivorous {get; set;}
          public Guid CommitId {get; set;}
          public long Version {get; set;}
       }
    }

This would mean that the external component that asks my herbivorous to eat grass should know that in this bounded context we talk about a cow. in the former command, we were talking about a general Herbivorous behavior, so the context of my call was not important. We knew that we needed herbivorous to eat grass that was all.

This is my main problem possible leakage of domain specific from one bounded context to another. Actually It might mean too that I cannot support abstraction when working on interfaces between several applications. I wonder...

Or I could just accept any event when rebuilding my herbivorous. It means that if it does not find a method to apply this event to, he will goes just fine trying to apply the next one of its stream. This is the real simple solution, but it does not put me at ease to know that events might not be applied ( and not producing errors) when rehydrating my object. (Actually the more I think about it the less I feel guilty..)

Thanks for your help, I am just beginning with these kinds of problem, and I would be glad to have some news from someone more experienced.

6
  • Sure you want a different command handler for each possible command, like in your example above? If yes, where is the problem, you could easily change it to create an instance of the related subclass.
    – Doc Brown
    Aug 30, 2012 at 17:29
  • Exactly, surely my mind was twisting on its own... Maybe I needed to explain it to someone so that I could go trough it. thanks for your help. I am going to delete this question, because it makes no sense actually... Sorry, for needing psychological help :D. Anyday you need someone to read you, I'll be there! :p
    – Arthis
    Aug 30, 2012 at 19:51
  • Well, my suggestion below was aiming at building a generic command handler, doing the object creation in a generic fashion and letting the individual subclasses themselves doing the Execute part of the command. That's how I would try to build such a system.
    – Doc Brown
    Aug 31, 2012 at 6:22
  • Interesting. I am trying to have a command handler foreach different command. Quite new to all of this, so my words might not fit in perfectly, but the command handler is kind of Application service. Well thanks anyway, I have understood your point, and if need is I would have ideas about how to build such a generic handler. have a nice day.
    – Arthis
    Aug 31, 2012 at 6:32
  • Possibly related: the principle of leaky abstractions. See en.wikipedia.org/wiki/Leaky_abstraction and joelonsoftware.com/articles/LeakyAbstractions.html
    – occulus
    Nov 28, 2012 at 16:14

2 Answers 2

1

First, add a virtual method Execute(cmd) to your Herbivorous, and put the code for the dispatching the commands to the command methods there. Then each subclass of Herbivorous can override that method and support all command methods specific to the subclass. Should look somehow like this:

public class Cow: Herbivorous
{
   public override void Execute(Command cmd)
   {
      if(cmd is ProduceMilkCommand)
          ProduceMilk(cmd);  // execute specific command
      else
          base.Execute(cmd); // delegate general commands to the base class
   }
}

For creating a new object from a given command, add a System.Type field to your cmd and use reflection to create an instance of that specific subtype of Herbivorous. Or use the prototype pattern to create a new instance (your command has to store a reference to that prototype object then instead). A third alternative is to use the abstract factory pattern, your command then will have to store a reference to the right factory subtype.

Of course, I guess I could give you a better answer if you had shown us how the command class currently looks like and how the commands are created. And your use of the "var" command in the code snippets hides some details which may be important for a more precise answer.

5
  • 1
    Ick. Execute(cmd) ...Really? I would have thought providing a more general interface like Mammalius would have been a better solution for the repository. Aug 29, 2012 at 20:45
  • @RobertHarvey: in my understanding, the OP wants the problem to be solved that different subclasses are responsible for different commands. To introduce a more general interface does not solve that problem, it just bypasses it. Perhaps that is what the OP wants (I guess not, but I may be wrong).
    – Doc Brown
    Aug 30, 2012 at 5:56
  • First of all thanks a lot for your answer. I have updated my question to be more precise. I'll have a look at what you tell me to understand how it might fit into my present problem.
    – Arthis
    Aug 30, 2012 at 6:29
  • I have changed several little things so takes the time to really read it carefully my changes were a bit everywhere.. ;)
    – Arthis
    Aug 30, 2012 at 6:40
  • Always nice to ask question because it forces you to explain everything and makes your mind much more clearer.. ;)
    – Arthis
    Aug 30, 2012 at 7:12
0

It's okay for your Herbivorous class to skip events it doesn't understand.

I learned a lot about this and related issues in this thread.

Hope that helps. I wish there were cows and horses in my domain!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.