How to return result while applying Command query separation (CQS)

1.6k Views Asked by At

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?

2

There are 2 best solutions below

4
On BEST ANSWER

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

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