Building an Online Store

Luis Martinez
8 min readJun 30, 2021
Figure 1. SellBy Online Store

During my second milestone project at Flatiron’s software engineering program I took the challenge of building an online store from scratch. I had some general ideas on how to get started from doing the Sinatra labs, but I decided not to read or watch any tutorials on the topic, just to see how far I could get. In this article I would like to share the most relevant moments in my journey of building my online store app, SellBy, using Sinatra and the MVC framework in Ruby.

ActiveRecord Migrations and Associations

The first thing to get started was to decide on the models that I would need for building the app and their corresponding associations. After some thought, I arrived at three models: User, Item, and Cart. The models and their corresponding associations are shown in the below diagram:

Figure 2. Schema diagram for SellBy.

As seem in the schema diagram, a user has many items, an item belongs to a user, a user has one cart, and a cart belongs to a user. The status column for items is an integer and it is used to create two enumerators: listing and purchased. The items column for the carts table is a string because it will be later serialized as an array (more on this in the last section of the article).

Architecture of the Life Cycle of an Item Object

One of the greatest challenges in building the online store was the design of the life cycle of an item object, that is, how to perform the CRUD actions on an item object effectively. From my experience in writing the code, I found that the id attribute of an item is mostly useful for the GET, EDIT, and DELETE actions; other than that, you need to rely on the other item’s properties for performing operations on an item. Before moving on, I want to specify that SellBy performs both functionalities: a user can purchase items from other users, and a user can sell items to other users., which is something to keep in mind in reading the remainder part of this article as the article is developed under this context.

To continue, this is the design I came up with for the life cycle of an item:

  • Upon instantiation, items are assigned a status of “listing” by default (the one more status is “purchased”). That is, when a user creates an item, the item is ready to be sold.
  • Rather than referring to individual item objects, items live under the umbrella of ‘Listings’. For example, when a user fills out the form to create new items in SellBy, they create a listing that has an item name, an item price, and an item stock.
  • Upon creation of a new listing, a corresponding number of new item objects as specified in the item stock number are instantiated and persisted to the database. To expand on this, consider figure 3 below:
Figure 3. Form for creating new item objects in SellBy.

Upon click on “Create Listing”, a total of 9 item objects will be instantiated; the only difference between these item objects will be their id; basically, 9 copies of Soap items are created.

  • After creation, updating an item presents some challenges, discussed in the next paragraphs.

What happens if we want to change (update) the stock for the above Soaps from 9 to 12 ? (Compare figure 3 and figure 4 below)

Figure 4. Updating the stock for a listing in SellBy.

This is the solution I came up with: count the number of soaps currently on the database for the user (an item belongs to a user), then subtract this number from the stock submitted in the form, that is, 12 –9 = 3. This means we need to create 3 new soap objects to get the 12 soaps. To accomplish this, upon the user clicking on “Update Listing”, find a copy of a soap item (it does not matter which one), then use the attributes of that soap item to create 3 new (identical) soap items or use the params from the form.

Now, what if we want to change (update) the stock for the soaps from 9 to 5? Similar procedure as above: count the number of soaps currently on the database for the user, then subtract the stock number submitted in the form from the current stock, that is, 9 –5 = 4. What does this mean? This means that we want to destroy 4 copies of the soap object so that the new stock is 5. To accomplish this, upon the user clicking on “Update Listing”, find all copies for the soap object for the user and destroy 4 of them.

The next one is (this is the generalization): What happens if we don’t want the name “Soap” anymore, but rather we want to change the name to “Soap Aromatic Fragrance”, with a price of $8.50 and a stock of 6? That is, what happens if we want to UPDATE the item name, the item price, and the item stock of a listing? (Compare figure 4 and figure 5 below)

Figure 5. Updating all properties of a listing in SellBy

This is the solution I came up with: take care of the item properties first and leave the stock last. That is, find all copies of the Soap item for the user, change the name and price of all of these copies to the name and price submitted in the form (do a .save for each item right after changing the name and the price); then if the stock submitted in the form is GREATER than the stock of the updated items, CREATE new copies using one of the updated soap items or the form params; otherwise, if the stock from the form is LESS than the original stock, DESTROY a corresponding number of the updated soap items (no more “Soap” but “Soap Aromatic Fragrance”).

Let’s see: What would be the action for going from figure 4 to figure 5? (There are possibly more ways of doing this, but this is the one I came up with; please let me know in the comments if you know other ones):

  • Find all copies of “Soap” for the (current) user in the database.
  • Change the name and price of all these copies from “Soap”, $3.25 TO “Soap Aromatic Fragrance”, $8.50 and do .save for each. The stock does not change (we have the same 12 objects still)
  • Then, since New stock (from form)(6) is less than Original stock(12), we are going to destroy 6 copies of the updated soap items; otherwise, we create copies for Soap Aromatic Fragrance, either using one of the updated ones or using the params from the form.

Last, when an item is purchased, the status column of the item object is changed from “listing” to “purchased”.

Hope this can be of help. Now let us go ahead with the shopping cart.

The Shopping Cart

As seen in the schema diagram from figure 2, there is no association between Item and Cart. Why not? (Again, there are likely more ways of modeling the cart) Well, suppose that we did establish an association between Cart and Item, say, Cart has_many :items, through: :cart_items, and Item has_many :carts, through: :cart_items. Then say that I add an item to my cart, as shown below

Figure 6. Shopping cart view in SellBy.

From the above figure, my cart has 4 “World Languages Book” objects, that is, cart.items yields an array of 4 item objects. Now suppose that I close the app in my desktop and go outside to do some things. While outside, the seller of this item (which is another user) updates the name of the item to “Social Studies Book”. Then I come back an hour later, open my desktop and go to my cart view. What I’m going to see? I’m going to see “Social Studies Book”, and I’l be asking myself why the name of the item in my cart changed. That is, changing the database changes the cart’s content. That is not desirable. How do we solve this? I came up with the following solution: we need the cart to be in a FROZEN STATE, independent of the database. Instead of cart.items yielding an ActiveRecord Association array, we want cart.itemsto be a plain ruby array, without ActiveRecord, and this will give us a frozen state for the cart: once an item is pushed to the cart.items array, the array won’t change even if the pushed item is updated at a later time, and the user will see the same item from the last time the user viewed the cart.

To create an items array for the Cart model, we use the ‘serialize’ keyword on the Cart model, as shown below

Class Cart < ActiveRecord::Base
belongs_to :user
serialize :items, Array
... more code
end

From the schema from figure 2, carts has an itemscolumn set to “string”. The serialize method is used on this column in the model to change the column from a string to an array. Then when we call, say, fred.cart.items we will get a plain ruby array that we can push items to.

To finalize, how do we update items from the cart.items array? I came up with this solution: when the user first clicks on the “Add to Cart” button for an item, push the item object to the user’s cart.items array. Then, when the user wants to update the item quantity in the cart, say, for example, from 1 to 4, do not create new item objects in the database, just grab the item from the cart, and then push the item as many times as the difference of the original quantity from the updated quantity. There are many other questions that could be asked about the cart, but I think they will not be addressed in this specific article.

You can find the git repository for SellBy at this link.

--

--