Project Solution: eCommerce Class Diagram
In this guide we're going to walk through the solution for the coffee ordering system's class diagram.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

Now, this diagram is even larger than the Twitter project, and it's for a good reason. One of the big items that I wanted to focus on with this is: being able to model what a database looks like for an eCommerce system. One of the very common questions I get from students is how to build out an eCommerce system. And one of the first things I'll usually tell them is to build out the model--to be able to structure the model because that is going to take you very far down the line in being able to understand the structure of the application. So that is something that as you have gone through this you probably had to spend quite a bit of time in building this out elements such as multiplicity obviously are very important in this. They are in every class diagram. But for this, you also have elements surrounding items such as data normalization which is the process of structuring a database in a way that conforms to industry best practices. Now if you're using Lucidchart in screen real estate is valuable then if you come up here you can click on full screen and this gives us a little bit more room. And I'm also going to come down here and give us some zoom. Kind of like we did with the Twitter application.

We're going to pick and choose different classes and focus on those we're not going to talk about every single class mainly because there's going to be duplications throughout the entire set of the explanations so I'm going to focus on what makes this type of class diagram different.

large

I'm going to start off with the taxonomy. This part is, I think, one of the trickier components of this entire system because it utilizes a situation where we don't just have a traditional "one table is related to another." In taxonomy, if you remember back to the instructions, what this represents is a high level abstract kind of class. So this means that we're not going to go and create a set of taxonomies. Instead what we're going to do is we're going to create categories and we're going to create tags, and both of those classes will inherit from the taxonomy class.

This is one of the most important concepts in object oriented programming, which is the ability to find situations where you have one class that can be a parent class and then build out custom versions of that. There are times where this type of class is still a class that you're going to build objects from. So imagine that you had a User class and then you had an admin user that inherited from that user. You might still create user objects and then create a separate set of admin users. But in this scenario this is the way that I'd structure this is to make taxonomy an abstract class. And if you've never heard that term before what that means is it's a class that gives a nice set of foundational rules for how all of the child classes need to behave. It gives items such as an ID, a name, a description, things that are going to be common among all the child classes. But in the program you will never create a taxonomy directly. That's the definition of an abstract class.

Instead, you will create categories. The category class is going to inherit from taxonomy and it's going to add some other elements. So it might say left or right or any of these kinds of items. And those are just thrown out as examples. So this could be quantity, it could be any kinds of things that you feel like a category should represent.

Now tags are different. Both a tag and a category are a type of taxonomy, but they have their own unique qualities. So, here, a tag might just be a different type of weight. So if you wanted to sort your coffee and be able to see all of the items that are over a pound then that's where the tag would come into play. Whereas, with a category, this might give you the type of sorting where you can go and say, "I would like to have only South American coffee blends." Some type of behavior like that.

Now, both, technically at a high level, are a giant category, but they are different enough where they should be split out. And there is one other item I want to point out here before switching. We have a weight and a calculateWeight() that has a different symbol in front of it. Notice with most of these we have plus symbols. What the plus represents is that these are public attributes. What the hash represents is that these are protected.

What that means is that, say that you have another class that inherits from tags. Say that you had some other type of customization where it might be something like 'mobile' tags or something related to that. Then, those inherited classes can call on weight and calculateWeight(), but the objects themselves and any outside systems cannot. These are specifically attributes and operations that are only available to this class and any child classes it may have. And that is how the visibility process works inside of object oriented programming.

Now, going up to Inventory/Product, you may notice that we have the ability to connect products with taxonomies. What that means is that a product can have categories, and it can have tags, and it can do it through the InventoryCategory join table .

You may notice I didn't add the multiplicity elements. And the reason for that is because, one, whenever I have a join table like this, it's always going to be a many-to-many relationship. But the other reason is because this is a little bit more complex. We are connecting a product with a class that will never exist. We'll only have different objects from the inherited classes that will be connected. So I also added this note that says that DB entities are going to implement these many-to-many relationships. And if you remember back to when we walk through the Twitter example, whenever I use a join table I like to use these dotted lines to represent this is not a class it's going to have functionality, it simply is going to have a role where it joins tables together. So we can have a inventory item, and then it could have a category or a tag.

Moving over to the User class, this gets into the discussion of database normalization. Here, you notice that we have a user that has an address, that a user can have many addresses, and that an address can have many users. And if you think that that's weird, think about a real world scenario. Take, for example, Amazon, who pretty much has everybody in the U.S. in their database. Every time that you move someplace else and someone else moves in to your previous address you do not want to say that this one user is the only person that can have that address. That will introduce a very sneaky little bug into your program, because when a user changes their address they're going to get a weird error, because the system is going to say, "I'm sorry. There is already a user with that address." And so that's something that you want to protect against, which is why this is a many-to-many relationship.

Now you might think that city and country are not needed here. And in that regard you may also think that none of these items are needed. There are plenty of times where I've seen developers that structured a database and they loaded up the address, the city, the country, all of those elements inside of the user table. Now that is something I want to highly discourage because you may want a log of everywhere that a user has lived, or, going back to the Amazon example, if you've ever used Amazon you know that it gives you the ability to send to multiple addresses. So I may want to have one address for my home, another address for the office, and other one for a family's residence when I'm sending something to them, and I want to be able to reference those. And that's a reason why I definitely wouldn't put it in the Users table.

Now, why do we need a city class and a country class? Well the reason for this is that it's a part of integrating database normalization. Now, you technically could just tell the user "Type in your city and then type in your country" and let them have at it, and you'd only have this one address class and an associated table. But I personally would prefer to split those out because they're easier to control when all of those elements are in their own type of table. But also it gives us the ability to validate the data. Imagine a scenario where you let users type in their city and country. If they make a typo and they spell their country wrong or they spell their city name wrong, then what could happen is that shipment would go out and it would get returned to you because it had an incorrect address. Whereas, if you keep an entire library of all the countries that are available and then you keep a list of cities--and you may also have seen where the type of process where you can type in your zip code and it will go find your city, state, and country and load all of those in. That is all made possible by splitting these up into their own classes.

So that's something that is definitely important when it comes to structuring your database is to think about how is it important to split up my data. Is it something where all of these elements are simply items that people can type in freehand, or do I need to implement some type of data validation to make sure that it protects against user error and different elements like that.

The last item we're going to talk about is payment. Now if you've ever been to an application where it gave you the option to either type a credit card in or enter your PayPal details, you may have wondered how that would be designed. The way that I would personally do it is that I would create an interface class. You can see that I'm adding in the payment component to say that I'm building a payment interface, and there are going to be a few elements here that are needed. So we need them to type in the payment type, we need to know what the total is, we need to have a reference to the order, and then check to see what the status of the payment is, whether it was declined or if everything went through.

And then we're going to have a credit card class and a PayPal class that both interact with this interface. In other words, these two classes don't inherit from payment. So because credit card is not technically a type of payment, it's a payment source and same with PayPal. It, by itself, isn't a payment. And so what what this type of setup allows us to do is to have much more granular control over how we're setting up the system architecture. Because, for a credit card, this is going to have to go out and communicate with some type of payment gateway. So it's going to have to either interact with Stripe or Authorize.Net. Those are two of the most popular payment gateways out there. And so that means it's going to have its own mini application, in a sense. I's going to have to go connect APIs, it's going to have to wrap up all of the data, it's going to have to have error handling, all of those components right here. You don't want to mix this functionality with what you have with PayPal, because PayPal offers a completely different integration solution. So if these two were merged together and you just tried to create one payment module, and then tried to say, "OK, if a user selects a credit card then go build out this process, or if they select PayPal then go build out this other process," then you're going to end up with a very messy code base.

Instead, the way that I personally would structure it is that I would create classes specifically that implement the payment interface and then they can go and they can manage their own outside connections.

And then lastly, we have this PaymentStatus class that is going to give us the ability to track what the status of the payment is. The reason why I wanted to break this into its own class is because whenever you're talking about any kind of functionality that deals with financial transactions, you need to make sure that you have your entire system covered. You do not want to implement some type of solution where a user could perform a few tasks and get around having a bad credit card, for example.

Imagine a scenario where you didn't build out the right types of checks for when a payment didn't go through. So the card came back declined. But then you didn't build out the entire workflow for saying what steps need to be taken from the user in order to either go and select a different payment method or to simply quit the transaction. If you have holes in your system and you're not capturing this type of payment status correctly, then there might be a situation where someone with a bad credit card would still get their shipment and that is not what you want.

You want to make sure that any type of mission critical elements that really need a high level of security have their own class, so that you can wrap all of the functionality inside of that one class. That's going to make it much easier to understand. Remember, one of the biggest secrets to development is being able to break a system down into as small a set of pieces as possible, so that you can go out and you can focus on one task at a time. Then, in the future, when you want to make changes, you don't have to worry about how each element is intertwined. You can make one change without being worried about effecting another one. When you have a system like that it's called a highly coupled system. It means that if I make a change to this PayPal module it might go and break something in the credit card module. And if you had these both merge together that is a very likely type of scenario. But if you can separate them--and the further you can separate these items out so they do not depend on each other--the better your system is going to be in the long run.