public ActionResult AddToCart(int id) { ... cart.AddToCart(addedAlbum); ... }
This leads us to the AddToCart method:
public void AddToCart(Album album) { ... if (cartItem == null) { // Create a new cart item cartItem = new Cart { AlbumId = album.AlbumId, CartId = shoppingCartId, Count = 1, DateCreated = DateTime.Now }; storeDB.AddToCarts(cartItem); } else { // Add one to the quantity cartItem.Count++; } // Save it storeDB.SaveChanges(); }
We find that all we need is the id of the album and the id of the cart to add it to, and therefore we can define our command as:
public interface IAddToCartCommand : ICommand { String CartId { get; set; } Int32 AlbumId { get; set; } } public interface ICommand : IMessage { }
In order to share the reference to the Cart class we have to pull the Models namespace into a separate assembly out of the uber assembly the sample ships with. There are three major things to note in this code.
First of all in order to identify a message to the NServiceBus infrastructure, we must mark the message with the IMessage interface. This is how NSB will wire messages to message handlers.
The second note is that we've created an intermediary interface. We have done this so we can easily differentiate between commands and events. Commands denote one-way, point to point communication between known parties. Events denote one-way, one to many communication with potentially unknown parties. The other way we differentiate the two is to change how the verb in the name is used. Typically commands will tell the server to do something, ex. "AddToCart". An event will let us know something happened in the past, ex. "ItemAddedToCart".
Lastly we always use interfaces to define our message schema. If we do so then NSB can gracefully handle the versioning of messages for us. This becomes very important when we change our messages and we have to maintain backwards compatibility.
Now we can look at placing an order. The code for this is a bit strange as its broken up into two different transactions, one for the "header" and one for the details. The first part is in the AddressAndPayment method in the CheckoutController class, and the second part is in the ShoppingCart class:
public ActionResult AddressAndPayment(FormCollection values) { var order = new Order(); ... order.Username = User.Identity.Name; order.OrderDate = DateTime.Now; //Save Order storeDB.AddToOrders(order); storeDB.SaveChanges(); //Process the order var cart = ShoppingCart.GetCart(this.HttpContext); cart.CreateOrder(order); return RedirectToAction("Complete", new { id = order.OrderId }); ... } public int CreateOrder(Order order) { ... var cartItems = GetCartItems(); foreach (var cartItem in cartItems) { var orderDetails = new OrderDetail { AlbumId = cartItem.AlbumId, OrderId = order.OrderId, UnitPrice = cartItem.Album.Price }; storeDB.OrderDetails.AddObject(orderDetails); ... } //Save the order storeDB.SaveChanges(); ... //Return the OrderId as a confirmation number return order.OrderId; }
In order to preserve consistency in our database, we'll perform all these actions in a single transaction. This way we don't get orders without their details. Also by using durable messaging, we'll ensure that orders don't get lost. Within the code, the Order is constructed from the ShoppingCart object. Therefore, all we need is the id of the current cart, and we can look up the rest of the information server side(also reducing trips to the server):
public interface IPlaceOrderCommand : ICommand { String CartId { get; set; } }
In summary we've decided on how our schema is going to look so that we can now start putting these messages On the Bus! Next time we'll change the store front to put messages on the bus. Code can be found here
No comments:
Post a Comment