Home
Sign in Buy now

MVC & Application structure

What's a model

Revision
Master Fullstack Golang Logo

Watch for free

Create a free account to watch this video.

Start for free

Summary

In this episode, we explore the concept of models in Go beyond simple database table mappings, treating them as core system components that encapsulate business logic and data validation. We demonstrate how to create user and admin models with find and create functions, emphasizing that Go's type system allows us to enforce distinctions between conceptually different entities even when they might share a database table. We also clarify the difference between business logic (data shape and validation, which belongs in the model layer) and application logic (user operations and permissions, which belongs in controllers or services). Finally, we build a simple article model with dummy data to illustrate these concepts before connecting to a real database in future episodes.

Transcript

All right, what is a model? If you have ever worked with something like Laravel or Rails or Django you might say that it corresponds to a table in the database. Now even though there is often a one-to-one representation between a model and a database table, it's not the complete mental model. You should think of a model as these core components or concepts of our systems. Let's say we have a bank as we did in the previous module. We will have the concept of a user. Now a user can be many things. It can be an admin, a customer, an employee, or both an employee and a customer. These might all be represented by one database table called users, but conceptually there is a huge difference between an admin user and a customer. Now this is a contrived example. In production you might actually have different tables, but technically they could all be part of the same table but conceptually be different components in the overall system. So let's take a quick example and look at how we can illustrate this in code. Say we have this user model: type user struct with id int64 and name is going to be a string. Now the only way you should be able to retrieve a user from the database should be through the models package or through the models layer. So we need to define some functions to retrieve users. So let's say we have a function here called find user that takes in a context.context and then an id of an int64. It returns a function to find a user. Right, and then we would have the actual logic for querying the database. And then let's just return a dummy user. Let me say id is one, name is just going to be my name for now, and then also an error. I'm omitting the logic for actually retrieving elements out of the database because we will hook this up in the next module. For now I just want to illustrate the concepts. Same thing goes for creating a new user - this would also have to be done in this package. So we can create a new function here called new user and we will say again, let's take in a context.context and then a name. You return the created user or we return an error, and again you will have actual logic for inserting user here. For now let's just return a dummy user once more, saying the ID that's gonna be two, name is gonna be the name we provided to the function, and then we return nil. Again, all logic for fetching and inserting users or models in general go through this package, which also means that any validation logic or business logic needs to be defined within this package or this layer of our architecture. So here we would say, let's say we can't have a name that is an empty string and you can also not have a name that is less than one character, and we can also not have a name that is longer than 50 characters. And if any of these cases returns true, we're just gonna say return empty user struct and say errors.new "user name invalid, must be longer than one character and less than 50 characters." So let me just go give that a save, and now the validation logic ensures that we can only create a user through this package and the only way to create a user is through this function which has all of the business logic that defines the shape of the data. Now say we wanted to have admins in our system that can perform certain actions that a normal user cannot. We could include an is_admin field on our users table and then add it to the user model, and then we would check that field before letting the user perform those guarded actions. However, Go is a strictly typed language so we should rely on that instead to enforce data being correct. So let's say instead we introduce an admin model that will simply be one-to-one like the user but this is a distinct type. And then we can go down here and say find admin, and instead of returning our user we will return admin. Let's just return the ID we provided here, let's see, I don't know, let's just say Bob is the admin for now. So then we would have to be very explicit both about what you're asking for but also for the distinct type that we get back. And then we can use this admin struct as an input to those operations or those actions that would need admin level permissions. I hope this illustrates that the concept of a model goes further than just mapping to a database table. We'll continuously add models as we progress through the course, so hopefully by the end you will have solidified the concept. It's important to note that we do not handle application logic in the model layer, only business logic. But what's the difference? Business logic, for example, relates to the shape of the data, like how the name of a user had to be a certain length. It could also be how long our password should be or which characters it should contain. Application logic however will be more related to what operations a user can do. It's not limited to that, but it's a good example of what is application logic. So deleting a user would have to be done by an admin, which is application logic rather than business logic. These two concepts do run very closely together so it can take some time to understand the finer details, but a general rule of thumb is that if it relates to data then it's business logic, and if it relates to how an application functions, it's application logic. This does not go into the model layer and you would typically see this in the controller layer or maybe in a service layer. Now let's start by defining a very simple version of our article model. We're going to start by creating a struct called article and then we're going to say ID is going to be an int64, it's going to have title string, and we're going to have content also going to be a string. And then for now we need two functions: one to return all articles and one that returns a specific article. We will start by just returning some dummy data as we haven't covered how to interact with a real database yet. So let's start by creating the dummy articles. We're going to say var article1 equals article struct with ID one, title "sample article" and content "this is a sample article." Let's copy paste all of this and say article2, ID two, title "another sample article" and content "this is another sample article." Great, then let's define the model function that returns all the articles. That is simply going to be find articles and we're just going to accept a context here because we're going to need that for later. We're going to return a slice of article or an error. Again we are mimicking the real thing here. For now we could just return the articles, but when we get to the database we will also return an error. Here we're simply going to say return the article slice with article1 and article2, and then nil. And then we import the context. Now for find article, we can copy paste this. It's going to be find article and we're going to return one article. We will then need to specify what article we're looking for. We're going to be using the ID here, so we're going to say ID int64. And then we're just going to use a switch statement for now: switch ID, case article1.ID return article1, case article2.ID return article2. If we can't match anything we just return empty article and nil. We could technically say errors.new here but it doesn't really matter, this is just dummy data. And there we have it, so this is our article model for now, so we can actually start to put something on the page.

Episode Notes

Mispelled title of second article

Get the Full Course

Unlock all episodes and get lifetime access to course updates.

What others say

"With this course, I'm learning Golang + datastar while building my personal blog hitting several birds with one stone. It's a fantastic course."

Testimonial from ignacio
Ignacio Barceló
Bought Early Access

Video Completed!

Great job finishing this episode

Up Next

MVC & Application structure
What's a view