1

I created a query as follows:

public class FindPostEditModelByIdQuery : IQuery<PostEditModel> {

  private readonly ILogger _logger;
  private readonly ISession _session;

  public FindPostEditModelByIdQuery(ISession session, ILogger logger) {
    _logger = logger;
    _session = session;        
  }

  public async Task<PostEditModel> Run(int postId, out exception) {
    // Code here
  }

}

So basically on the constructor I am requesting through IOC what I will use ...

This approach allows, I hope, to use async in an easy way an keep things simple.

A few questions:

  1. In my controllers I will use many commands ... I would like to avoid having a lot of them on the constructor. Do you think injecting a factory that requests a command would be ok? How should I do this?

  2. Am I using async correctly?

  3. What do you think about passing the exception as a parameter? Would be possible to create something that would log an exception the momment it occurs on the Run methods. Does this even make sense?

UPDATE

What I post is a try to make it easier to use async when compared to what I am using.

I am using Queries / Replies and Commands (I called them orders at the time) and I have:

public interface IOrderHandler {
  void Handle(Order order);
}
public interface IOrderHandler<TOrder> : IOrderHandler where TOrder : Order {
  void Handle(TOrder order);
}
public interface IQueryHandler {
  Reply Handle(Query query);
}
public interface IQueryHandler<TQuery, TReply> : IQueryHandler
  where TQuery : Query
  where TReply : Reply {
  Reply Handle(TQuery query);
}

public abstract class OrderHandler : IOrderHandler {
  public abstract void Handle(Order order); // Handle
}
public abstract class OrderHandler<TOrder> : OrderHandler, IOrderHandler<TOrder> where TOrder : Order {
  public override void Handle(Order order) {
    TOrder torder = (TOrder)order;
    Handle(torder);
  }
  public abstract void Handle(TOrder order); // Handle
}

public abstract class QueryHandler<TQuery, TReply> : QueryHandler, IQueryHandler<TQuery, TReply>
where TQuery : Query
where TReply : Reply {
  public override Reply Handle(Query query) {
    TQuery tquery = (TQuery)query;
    Reply treply = Handle(tquery);
    return treply;
  }
  public abstract Reply Handle(TQuery query);
}

public abstract class QueryHandler : IQueryHandler {
  public abstract Reply Handle(Query query);
}

public abstract class QueryHandler<TQuery, TReply> : QueryHandler, IQueryHandler<TQuery, TReply>
  where TQuery : Query
  where TReply : Reply {
  public override Reply Handle(Query query) {
    TQuery tquery = (TQuery)query;
    Reply treply = Handle(tquery);
    return treply;
  }
  public abstract Reply Handle(TQuery query);
}

public abstract class Order { } // Order

public abstract class Query { }

public abstract class Reply {
  public Exception Exception { get; set; }
}

public interface IDispatcher {
  void Send(Order order);
  void Send(Order order, out Exception exception);
  TReply Send<TReply>(Query query) where TReply : Reply, new();
}

public class Dispatcher : IDispatcher {

  public void Send(Order order) {

    Type type = typeof(IOrderHandler<>).MakeGenericType(order.GetType());
    IOrderHandler handler = (IOrderHandler)ObjectFactory.GetInstance(type);

    try {
      handler.Handle(order);
    } catch (Exception exception) {
      ILogger logger = ObjectFactory.GetInstance<ILogger>();
      logger.Send(exception);
      if (Debugger.IsAttached) throw;
    }

  }

  public void Send(Order order, out Exception exception) {
    Type type = typeof(IOrderHandler<>).MakeGenericType(order.GetType());
    IOrderHandler handler = (IOrderHandler)ObjectFactory.GetInstance(type);
    exception = null;
    try {
      handler.Handle(order);
    } catch (Exception ex) {
      ILogger logger = ObjectFactory.GetInstance<ILogger>();
      logger.Send(ex);
      exception = ex;
      if (Debugger.IsAttached) throw;
    }

  }

  public TReply Send<TReply>(Query query) where TReply : Reply, new() {

    Type type = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TReply));

    IQueryHandler handler = (IQueryHandler)ObjectFactory.GetInstance(type);

    try {
      return (TReply)handler.Handle(query);
    } catch (Exception exception) {
      ILogger logger = ObjectFactory.GetInstance<ILogger>();
      logger.Send(exception);
      if (Debugger.IsAttached) throw;
      return new TReply { Exception = exception };
    }
  }

} // Dispatcher

A FEW NOTES AND QUESTIONS

  1. It is the dispatcher I am injecting on my controllers. And then I use it:

    _dispacther.Send(new FindPostByIdQuery(22));

  2. I am not sure if logging the exceptions in the dispatcher is a good approach. Basically I am logging all exceptions that occur on the service layer.

    One thing I was considering was having an ExceptionHandler which could be attached.

    But I am not sure if this could be possible. What do you think?

  3. I am not sure how to make my code to work with async if I need it.

  4. Does anyone knows how to test this pattern? It has not been easy for me.

Miguel Moura
  • 36,732
  • 85
  • 259
  • 481
  • Your `FindPostEditModelByIdQuery` mixes data and behavior. This makes it very hard test stuff in isolation and makes it hard to apply cross-cutting concerns. Instead, take a look at [this related design](https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92). – Steven Sep 09 '14 at 18:48
  • I added more information and why it has been hard to me to use async. – Miguel Moura Sep 09 '14 at 18:51
  • Okay, I'm sorry. It seems you actually read that article :-) – Steven Sep 09 '14 at 18:53
  • Hey, can you post or link gists of your complete classes? I might be able to take a stab... – Mrchief Sep 09 '14 at 23:39
  • @Mrchief Sorry for the delay ... Different time zone. I just updated my code with all I have (Let me know if you need more info). At the end I added a few notes and questions. – Miguel Moura Sep 10 '14 at 09:21
  • I've a rough idea, which I can update my answer with. It won't be copy/paste code so you may have to tweak it a bit to get it working, but hopefully it'll point you in the right direction. Let me know if that's ok with you. – Mrchief Sep 10 '14 at 14:02

1 Answers1

2

#1: Your controller should not be using that many injected commands. Constructor over injection is usually a smell that the object needs to be broken down further. Concerns like logging, exception handling are often cross cutting and they are better handled at an outer layer.

#2: There is barely any code to be able to say anything conclusively. Signature-wise this is ok, although like I said above, you can probably do away with out exception. The caller needs to handle the fault in a async chain using ContinueWith or similar.

task.ContinueWith(x => x.Exception.Handle(ex =>
                  {
                      logger.Error(ex.Message, ex);
                      return false;
                  }), TaskContinuationOptions.OnlyOnFaulted);

#3: Exception logging should not be the responsibility of the query object, rather something that sits above it - using action filters, or at the caller level or a service or better - using aspect oriented programming.

Update:

Take a look at ShortBus. Unless you want to DIY, this has all the things you're looking for, including async handlers, ability to bubble up exceptions and unit tests.

Community
  • 1
  • 1
Mrchief
  • 75,126
  • 20
  • 142
  • 189
  • I added more information on what I am using now and why I was considering moving. – Miguel Moura Sep 09 '14 at 18:50
  • I absolutely agree with @Mrchief on constructor over injection. Consider splitting the controllers in smaller parts. I would say that usually your controller should have just one command handler (or two at most). – Steven Sep 09 '14 at 18:56
  • I find this incredibly simple and easy to follow: http://stackoverflow.com/a/23534126/244353 – Mrchief Sep 09 '14 at 18:56
  • @Steven: And looks like I totally agree with you (see my previous comment!). I absolutely love your blog posts about "meanwhile on the query/command side..." – Mrchief Sep 09 '14 at 18:58