Feedback - nryckman/Heinz95729 GitHub Wiki

Jeez - the server side is gorgeous. Well done. The client side is well done too. Good luck!

CheckoutModule.cs

In CheckoutModule.cs, you have a lot of business logic, which belongs in the domain classes. Why? Your controllers have multiple responsibilities (SRP). If you want to update or change your web framework, you may have to refactor your controllers (OCP).

Think of the controllers as simple accessors. They should take traffic, get it to the features that it needs, and then convert the results of those features into something the client can understand (i.e. JSON).

For instance, the following code could be in a domain class (i.e. PaymentProcessor.FinalizePayment(cartId, cc);):

// calculate total cost of items
List<ICartItem> cartList = cartItems.Repo.GetCartItems(args.cart_id);
int cartCount = cartList==null?0:cartList.Count;
int totalCost = 0;
for (int i = 0; i < cartCount; i++)
{
    totalCost += Convert.ToInt32(cartList[i].Price * 100) * cartList[i].Quantity;
}

// charge credit card
if (totalCost > 0)
{
    System.Diagnostics.Debug.WriteLine("cost: " + totalCost);
    StripePayment payment = new StripePayment("sk_test_S9tiUYvwqfDyIhFWS7VReNzF");
    StripeCharge charge = payment.Charge(totalCost, "usd", cc, "Test charge");
    System.Diagnostics.Debug.WriteLine(charge);
}

... and this could be in the CartItemDomain:

// mark cart items as purchased using datetime stamp
for (int i = 0; i < cartCount; i++)
{
    cartList[i].PurchaseDate = DateTime.Now;
    cartItems.Repo.Set(cartList[i]);
}

Client Controllers

Each of the client controllers creates a viewModel explicitly (i.e. cartController => onAddProduct, checkoutController => checkoutViewModel, historyController => historyViewModel). These would be more appropriate in a view module (SRP). You could then pass a factory into the controller module (i.e. makeCartItem) and call that instead. For instance:

define('models/cartVw', {
    init: function (ko) {
        "use strict";


    var cartVw = function (template, data) {
        var self = {};

        self.template = template;
        self.data = data;
        self.calculate = function () {
            var unit_costs = [];
            $("input#unit_cost").each(function () {
                unit_costs.push($(this).val());
            });

            var quantities = [];
            $("select#quantity").each(function () {
                quantities.push($(this).val());
            });

            var count = unit_costs.length;
            var total = 0;
            for (var i = 0; i < count; i++) {
                var subtotal = unit_costs[i] * quantities[i];
                total = total + subtotal;
                $("span#subtotal").eq(i).text('$' + subtotal.toFixed(2));
            }

            $("span#total").first().text('$' + total.toFixed(2));
        };

        self.udpateQuantity = function (obj, event, product_uid) {
            if (event.originalEvent) { //user changed
                var selector = 'form#form_' + product_uid + ' #quantity';
                var quantity = $(selector).val();
                var cart_id = $.cookie('cart_id');



                if (cart_id !== undefined) {
                    $.ajax({
                        url: '/api/cart/update/' + cart_id + '/' + product_uid + '/quantity/' + quantity,
                        method: 'POST'
                    }).done(function (data) {
                        self.calculate();
                    });
                }
            }

        }

        self.after = function () {
            self.calculate();
        };

        return self;
    };
    
    return {
        cartVw: cartVw
    };
}
define('controllers/cartController', {
    init: function ($, routes, viewEngine, CartItems, CartItem, cartVw) {
        "use strict";

        // removed for brevity

        // GET /#/cart
        routes.get(/^\/#\/cart\/?/i, function (context) {  // /books
            var cart_id = $.cookie('cart_id');

            if (cart_id !== undefined)
            {
                $.ajax({
                    url: '/api/cart/list/' + cart_id,
                    method: 'GET'
                }).done(function (data) {
                    var results = new CartItems(JSON.parse(data));

                    if (results.cartItems().length > 0) {
                        var viewModel = cartVw('t-cart', results);
                        viewEngine.setView(viewModel);
                    } else {
                        viewEngine.setView({
                            template: 't-empty',
                            data: { searchterm: 'blah' }
                        });
                    }
                });

            } else {
                viewEngine.setView({
                    template: 't-empty',
                    data: { searchterm: 'blah' }
                });
            }
        });
    }
});
⚠️ **GitHub.com Fallback** ⚠️