1

I am separating my query and command on service side like this:

public class ProductCommandService{
    void AddProduct(Product product);
}

public interface ProductQueryService{
    Product GetProduct(Guid id);
    Product[] GetAllProducts();
}

Command Query Separation accepts that a method should change state or return a result. There is no problem.

public class ProductController: ApiController{

    private interface ProductCommandService commandService;
    private interface ProductQueryService queryService;

    [HttpPost]
    public ActionResult Create(Product product){
        commandService.AddProduct(product);

        return ???
    }

    [HttpGet]
    public Product GetProduct(Guid id){
        return commandService.GetProduct(id);
    }

    [HttpGet]
    public Product[] GetAllProducts(){
        return queryService.GetAllProducts();
    }
}

I am applying command query separation on service side but not applying in controller class. Because user may want to see created product result. But commandService works in Create Controller Action metod and does not return created product.

What will we return to user? AllProducts? Will CQS applly about application lifecycle?

Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121
barteloma
  • 6,403
  • 14
  • 79
  • 173
  • 1
    Be careful with your terminology QCS is not CQRS. Anyhow, see this blog post: http://blog.ploeh.dk/2014/08/11/cqs-versus-server-generated-ids/ – David Osborne Jun 22 '15 at 10:34

2 Answers2

1

In such scenario I usually go with generating new entity Ids on the client. Like this:

public class ProductController: Controller{

    private IProductCommandService commandService;
    private IProductQueryService queryService;
    private IIdGenerationService idGenerator;

    [HttpPost]
    public ActionResult Create(Product product){
        var newProductId = idGenerator.NewId();
        product.Id = newProductId;
        commandService.AddProduct(product);

        //TODO: add url parameter or TempData key to show "product created" message if needed    
        return RedirectToAction("GetProduct", new {id = newProductId});
    }

    [HttpGet]
    public ActionResult GetProduct(Guid id){
        return queryService.GetProduct(id);
    }
}

This way you are also following POST-REDIRECT-GET rule which you should do even when not using CQRS.

EDITED: Sorry, did not notice that you are building an API, not MVC application. In this case I would return a URL to newly created Product:

public class ProductController: ApiController{

    private IProductCommandService commandService;
    private IProductQueryService queryService;
    private IIdGenerationService idGenerator;

    [HttpPost]
    public ActionResult Create(Product product){
        var newProductId = idGenerator.NewId();
        product.Id = newProductId;
        commandService.AddProduct(product);

        return this.Url.Link("Default", new { Controller = "Product", Action = "GetProduct", id = newProductId });
    }

    [HttpGet]
    public ActionResult GetProduct(Guid id){
        return queryService.GetProduct(id);
    }
}
  • Looks like make sense, you are generating your object ID manually. This possible an ID duplication on database. May be billions of records. How do you solve it? – barteloma Jun 22 '15 at 08:45
  • There must be a DB unique constraint to prevent data inconsistency of course. Id generator in its turn must guarantee Id uniqueness (e.g. must be thread safe). With GUIDs you get this out of the box. Harder to do with Integers as Ids but still possible (e.g. use one global identity counter, could be a simple SQL table with one identity field). – Alexandr Sugak Jun 22 '15 at 08:46
  • Do you mean: If you select ID type as GUID, duplication is impossible. – barteloma Jun 22 '15 at 08:58
  • Well, technically speaking, not "impossible" but [close to this](http://stackoverflow.com/questions/39771/is-a-guid-unique-100-of-the-time). – Alexandr Sugak Jun 22 '15 at 09:05
1

Command methods does not return anything, only changes state but command events can return arguments that you need.

commandService.OnProductAdd += (args)=>{
  var id = args.Product.Id;
}