Domain Modeling for JavaScript Apps with Mirage JS

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so we just uploaded a virtual training here it was originally gonna be an in-person training at ember conf but because the conference was cancelled we went ahead and made it a video and now we can share with everyone and even though it was going to be delivered to ember developers there's really very little having to do with ember specifically so I think there's a lot of value in here for anyone building JavaScript apps with ember or react or anything else so I hope you enjoy and learn a little bit about domain modeling with Mirage Jas hey everybody welcome to the domain modeling training here for ember conf 2020 or corona conf 2020 sorry we couldn't see you in person this year we were really looking forward to as we do every year but I think we have a good training here for you and yeah so let's get right into it so you can find the training here on our ember map github at ember comm 2020 and I'll have a link to this you know right in the email and this is the repository with the demo for our training and so this is an ember app so the first step would be to go ahead and clone this go ahead and yarn install and then once you have that running you should just be able to run yarn start and kind of kick everything off so while we're doing that I'll just explain kind of how this is going to go the readme here is kind of a the overview of our training and that's also in the repository of course so you can just pop that open if you want to follow along here so let's see how this is doing once this gets going we should be able to visit localhost 4200 and if your screen looks like this that means everything is working so I'm just gonna go ahead and make this full screen just so we have a little bit more room to work with here and let's take a look at this overview so domain modeling what are we really talking about here well the reason domain modeling is important is because you know traditionally with server-side rendered apps like let's say Wikipedia you know it's just talking to a server when the user makes a request for a web page the server lives right next to the database and in most web apps the database is the source of truth now depending on your background you might have some experience working with databases and servers or you might just be focused more on the front end and only interact with server data over an API but you've maybe never done it dealt with it yourself so in most web apps the database is the source of truth and with traditional server-side rendering frameworks like rails and laravel have this luxury where they get to talk directly to the database as soon as they're rendering a page and so a lot of the issues that we have when building rich JavaScript apps like ember apps don't really come up because you know as soon as I go to a Wikipedia article the backend server is gonna request the latest version of that article from the database render it to an HTML page and then send it up to me that's a simple model and it makes it really easy to get started right but it's also limiting in the sense that when the user gets that page from Wikipedia you know that page could just be sitting there and if there was an edit made and the backend let's say the source of truth the database were to update well the user is just looking at a stale page right and so in order to get fresh data the user would have to command R would have to refresh the page and get a rerender from the server so it's simple right there's trade-offs and everything it's a simple model but it really puts the burden of getting fresh data on the user whereas you know with something like an application like slack that is giving me real-time messages as they change that model wouldn't really be possible wouldn't really be possible to require the user to have to go back to the server on their own initiative each time they wanted to see fresh data and that's exactly why we build JavaScript apps because it enables this richer experience where we can do a lot more on the user's behalf but again this comes with trade-offs as well because now we have to be intelligent about how to get data from our source of truth our database and our server to our all of our clients and now that our clients are kind of disconnected from our back-end systems each client that is running our member app is working with its own local set of data and is now disconnected and so that's really the motivation of this entire question of domain modeling because we now have to answer this question what's the best most efficient way to get data from our source of truth our database to each of our clients when they need it you know sometimes it makes sense to render from cache on the client you already have the data you need but sometimes you want to push new data as soon as it comes up just like in slack so that's really the motivation behind domain modeling it's how best to organize and structure our data so that it can be efficiently sent to our clients so let's take a look at this demo app here and if everything's running again you should see something that looks like this we're gonna go through all these exercises over here so you can always pop this out and jump around or you can just use this to to change as we go along but let's take a look at this exercise one here so this is a Mirage inspector and we're gonna be learning about domain modeling with Mirage because Mirage is really kind of focused on making the server as simple as possible for the needs of the front-end developer right there's a lot of complexities that come with writing a real production server and a database server which power most web applications and you know things like adding indexes to make sure your database queries are fast and that's kind of a back-end concern and those things are important but they're not as important for the day-to-day life of a front-end developer really mirage is about abstracting away as much of those details as possible while still exposing the concepts needed and the primitives needed for us to get a high fidelity experience as front-end developers working against the mirage server so that's why we're going to use mirage to learn about domain modeling here and so if you've only ever used mirage with Ember you might be used to using ember CLI mirage but under the hood mirage Jas is the library that powers and it works in any framework so we're just gonna see all these examples using Mirage gas directly so this is kind of an example of the simplest Mirage server that you could configure you know it just has this routes hook and it has one API endpoint here so our server is kind of running on all these pages it just uses the the config from the current page here so our server is running and then here we have on the right side our kind of mock client so you can think of this as your ember app the the JavaScript that's going to be making the network requests to your back-end and so we can make a request here we can see we have a get request to find a slash movies so we can type in slash movies and see that it returns the JSON here so it's just returning exactly what we defined here in our route and we can also see what headers we have and the status code right here and if we were to try to make a request to an unhandled endpoint we'll see that so we have some some error handling and all that kind of stuff but this is basically just simulating this and because this is exercise one we can come back over to our app here and where all these exercises are located is an app exercises so we can pop open exercise one just like this and you know if we wanted to change this which will we'll be doing throughout the training we could add you know change this and let's say dark knight year 2008 save that and if we come back this will reload and now when we try to get movies we'll see all the data here so this is kind of how we're gonna use this inspector training app now you'll notice that we have a database tab here and that's because Mirage has you know a simulated database but it says the database is empty and that's because we haven't defined any models for our Mirage server so let's flip over to exercise 2 and take a look here this exercise is just about looking at how to implement kind of a basic crud server with Mirage so here we can see we have many more routes defined and we have this new serializers option but if we look at the database we see that it's still empty and the way we can create database tables in Mirage is to define models so let's come back to our code and we'll open up exercise 2 and you can follow along here so at the top of exercise to where we import these from Mirage we'll go ahead and import model as well and then we'll create a new option for our server definition called models and we can see that these routes here all interact with user so that's the model that we want to create so if we define a key here called user and we just say that's a model and save that now when we come back to our page we'll see that the database tab says the users table has no record so just with adding this we've basically told Mirage to expect there to be this kind of user resource in our server ok so we have a user's table but we have no records how do we create some records well back in our code we can also use a new top-level hook called the seeds and this is a hook that takes in a server and lets us create data so we can create a user by calling server dot create user just like that if we save that and come back now in our database we see we have a record this is a pretty boring record it just has an ID of 1 so let's give it a little bit more data and with Mirage you can just kind of define attributes like this on the fly so we'll say name is Sam and let's create another one name of Ryan so now our users table has two records and the IDS are being Auto generated by Mirage again this is how most back-end systems actually work the ID this is actually kind of an important concept for this course because ID is an identifier it's a unique identifier this is the globally unique representation of this object in our system and right now it exists as a record in our database but Mirage is just going to give that to us automatically okay so now that we have data in our users table here we can come back and take a look at some of these routes and these are the kind of five standard restful routes in a rest back-end so that's it let's look at this one first we have a get two slash users and it returns schema dot users dot all so if we type in users here and hit enter there we see Sam and Ryan so this is how you interact with the state of mirages database we can also just get a single one let's go ahead and grab Brian at ID 2 we can create a new user so if we go to post slash users will this expects a certain format in this case it's gonna look a lot like our kind of payload down here when we when we send a get request so we can go ahead and say name is yahuda and I'm just gonna go ahead and zoom out a little bit to give us some more room and now if I send over this request we'll see that this was a 201 created and the response told us the name was Yehuda and the ID was 3 so even though we didn't specify an ID here if we take a look at our database we see that this route handler right here went ahead and created the new record into our database Mirage assigned an ID of 3 and we got it back here and so you know we can create records just like this just like you'd expect and we can patch records as well so we can update Sam let's say to Sam wise and if I go ahead and send this out then we'll see that that updates in the database as well and of course we can delete users too so say bye-bye to Sam so that's kind of the basics of a model and how it creates the data here in the database and how these kind of five you might hear them called restful or resourceful routes exist and again if you were to look at rails or laravel kind of starting guide you would see something very similar here how to perform what we call basic crud operations on a resource so the way to think about this is that we have this database table in our back-end and has this data and the question is how do we expose this data to be read and manipulated by clients and this is the conventional way to do rests and it takes advantage of these verbs here and also this resource and because this is so conventional Mirage actually has a shorthand so instead of having to learn how to write all these we can actually go ahead and delete these and say this dot resource user so this is basically kind of a way to say we're exposing a user resource from our server to our clients and so if we come back we see that that's really simplified this config here we still have this data but if we were to go ahead and get the users then we see we have the same result and we can do all the other same things as well so we could send a delete request and say bye to Ryan and there we go and now when we get all the users we just get Sam so and I'll just refresh this to get Ryan back so this is pretty cool and you know again the purpose of this course is to kind of get you familiar with how ultimately this data is stored in the backend so we can see that this is the source of truth and and when our client app sends a request to users we're kind of getting this snapshot of the database at a point in time and so just like in the server rendered app case you know our client app can now use this and render on the client you know that the the users in the system now in this case we see basically everything in the database so this is you can think of as like the easy case the easiest case you'll ever encounter because our client is able to get basically a copy of the database in a certain format you know in real-world situations the database usually has way more records than can just be sent to the client and so that's where some of the tricks come but this is kind of the simplest case so let's go ahead and go to exercise 3 and you're just going to practice creating a model on your own and kind of practice creating some data and exploring the inspector so if you come back to the instructions here you'll see that exercise 3 so let's create another model called a message and this is going to be you know throughout this training we're gonna look at something that looks kind of like a slack or a discord app so they'll be users there'll be messages things like that and so here a message just looks like this pretty simple just one kind of column one attribute called text with the text of the message so define a message model and create some messages and seeds add a resource to routes for the message resource and then explore the database tab and making requests so again back in your demo app here you should be on exercise three and over in your code you can just pop open exercise 3 exercise and make changes to this and you'll see those changes right here and it will update this and then the once you are kind of comfortable you can just peek at the solution and see where we ended up but go ahead and pause the video now and just play around with this for a little bit all right welcome back so let's go ahead and do this so if we want to make another model we can just come here and make a message model let's go ahead and create Sam and Ryan and let's also create a message so we'll create a message with text attribute hey what's up oh you know Corona and we can come down here and expose the message resource as well so with that change we can come back and check out exercise 3 and now if we look at our database we've got two tables so we have users and we have messages here and we can perform all these kind of requests to our messages just like we can to our users so we can get all of our messages we can get a single message we can delete a message and we can see that everything is working ok so again this is very similar to how this would work on the back end having two separate tables for these two conceptual resources in our system now let's go ahead and go to exercise 4 and talk about associations so maybe if you thought ahead the natural question that you might already have is let's say that we were actually building an app like slack and you know from our client apps perspective we need to render all the messages in the general channel and so we need to fetch all that data and we need to fetch it in a way that's going to make our lives easy right well if we look at the data that's being stored here in the database we have these two users and we have these messages but right now they're not associated with each other these messages are just kind of by themselves we don't actually know who wrote each message and so our database is incomplete in a sense that the source of truth is kind of inaccurate because it doesn't really make sense for a message to exist without a reference of some sort to user and this is kind of one of the first points of domain modeling domain modeling is really again how to organize this data there's a million ways we could organize this data we're learning to do it in kind of the most standard and conventional way because there are so many problems that you encounter based on how you decide to store and share your data that these conventions and standards have evolved over time and give enough given us these really rock solid principles for how to do it so there's a million ways we could choose to associate a message with a user but the way we're gonna do it is by using a foreign key so what does that look like well let's take a look here at users users - is Ryan right so the user with the identifier of - is Ryan so there's never gonna be another user in this system with an ID of - so let's say this first message was written by Ryan how might we store this well using a foreign key would look something like this user ID - let's save that and now over messages whoops I think we're on exercise 4 and I think I was still on three so over in four here we'll say user ID is two and now if we take a look we see we have a new kind of column in our database and this record has a user ID - so this is really a reference as a pointer to Ryan and so we could go here and say this has user ID - this has user ID one user ID - and user ID one so this is kind of a conversation between Sam and Ryan right and now our database looks like this and now if we were in our Ember code and we needed to fetch the data to render the whole conversation how might we do that well we could get all the messages and if we send a get to all the messages there we've got four of them and the messages have the text that we need to render and they also point to the user ID but we probably want to show who the name of the person is so we also need the users and so we can go ahead and fetch the users and now between these two requests we have everything we need right because we know user one is Sam and we know user two is Ryan so on the front end if we made both of these requests we could stitch these two together and then render in our templates the conversation right and so this is typically how this data is stored now this notion of a foreign key is again it's just a fancy word for something that points to a unique record elsewhere in the system it's so common that it's actually built into Mirage and so the way we can take advantage of this convention is by using a belongs to relationship so what we're gonna do is import belongs to from Mirage J s and what we can do is come down to our message model which is the model that owns the foreign key here and we can call dot extend and we can say that it has a user reference which is a belongs to relationship belongs to means that this user property points to a single model of the other type so let me just comment that out really quick and let's get rid of these first and go back to just having messages with text so you can see how this changes the database so I've saved this and back in the database we're just back to kind of these unassociated records right messages just have a text field and then ID and now if we add this relationship to our message and save it if we come back to messages we'll know that Mirage has went ahead and added a user ID column for us without us even having to write user ID anywhere and again that's because that's exactly what this helper is doing it's establishing this convention of saying you know what messages now have a relationship to a user and so they need a foreign key to store that information and so what's cool about this is that well we could still do something like you know user ID is to here but - we just have to know that that's Ryan instead what we can do is say let's get the user model from Mirage and we'll pass that in as the user for the message here and this attribute here matches this one right here so if we save that and come back and take a look look at that user ID is - which is pointing to Ryan and so we can do the same thing like this this user is Sam this user is Ryan and this user is Sam and so and so if we look at the data we would expect to see the user IDs just like before and we do so this kind of belongs to abstraction is really just you know a light layer around what is ultimately these foreign keys in a database so again if you've never worked with this before this is the magic under the hood when you make a request and get this big graph of data in your ember app maybe that you're used to working on day to day at the end of the day all this data is being stored in separate database tables that look just like this another term you might hear when you start diving into kind of server-side code or concepts is an ORM which stands for an object relational mapper which is exactly what we're doing right here we're basically defining our schema our domain model for our resources and by wiring up these relationships we are mapping data to different objects and that's why we can basically work with these objects like this this is a user object you know a model this is a message model and that's exactly what an ORM does it's just like ember data if you're used to working with that on the front end it wraps the underlying records from the database or the data store around objects that lets you work with them in a relationship aware way so this is pretty cool and we have our data set up in our database we can still get our users we can still get our messages and our client could basically stitch these together to get the data it needs to render the channel page but the the relationship actually does a little bit more for us as well so again we have these two resources exposed from our routes here so check this out we have Sam and Ryan and they each have two messages right what if I were to send a delete request to user slash to which is Ryan well look at that - a for the record was deleted so if we go back to users there's only Sam but notice that the user ID field was updated in the messages table for Ryan's two messages so this is something else that Mirage and again other back-end systems are usually capable of doing is basically keeping these foreign keys in sync and ensuring that there is consistency in our database now in a real back-end system you might have even more control or you want to add more constraints and say you know what a message should never exist in our database without a pointer to a user because that's basically just not valid so you might hear this call as an orphan record in our database and a lot of systems have support for cascading deletes basically if I were to delete Ryan the user make sure we also delete all of his associated messages in our back-end system but basically you can see here that Mirage is doing some work for us to keep these foreign keys and sync which is why you want to lean on these conventions of relationships and foreign keys again this is all coming back to this notion of domain modeling and how we can just you know stand on the shoulders of all the precedents that we have so we're not reinventing the wheel with how to keep these things in sync and how to expose relationships over the network it's really easy to fall into that trap because we write backends for every app that we write and we can choose to do it however we want and so the real value here is learning in these conventions and kind of learning how to stay within these lines okay so that was exercise 4 if we go over to 5 and this is going to be a practice exercise where you get to practice making it belongs to relationship if you come over here to the readme and look at exercise 5 let's say that users can also have activities and an activity can be like a mention or a reaction or an upload and so this is going to be a new model in our system and so what you're going to do is create a new activity model associate with a user and go ahead and create some activities for a user and again this is kind of the structure of an activity it has a kind property and then it also has a foreign key to a user so go ahead and pause the video and play around with that for a little bit alright time to compare our work so let's come over here to exercise five and basically we want to create a new activity model it's going to look like this and we also want it to belong to a user so rollright belongs to here and we can come down and server it out create an activity and let's say this is for user Sam and it is a mention let's take a look at our database here we see activities mention and that one points to Sam and we could also come down here and expose the activity resource from our server and you know you can imagine doing this so that again if we were building something like a page to show all of our latest activities now our client has the ability to query that and again if we were to look at this let's go ahead and delete user slash one and we see the user ID is cleared from the activity as well as these messages so just another little example of this belongs to relationship which is you know really kind of fundamental to this whole thing so let's go on to exercise 6 here so the other main relationship type is a has many relationship and you might see this word association relationship if you see this kind of in any of these areas mirages rails laravel number data that's the same thing as just a synonym for each other and so in exercise 6 we are kind of back to just having these two models a user and a message so we've removed the belongs to from the message side and we just have this data that's kind of unassociated with each other so the other way we could choose to store this is by adding the relationship to the user side so instead of adding instead of extending the message let's extend the user and we'll import has many from Mirage Jas and we'll say a user has many messages so we'll give it a message as property and we'll say house many let's come back and see how that changed the database now we see that Mirage has added a message IDs column to each one of our users so the has many side is gonna give us an array of foreign keys whereas the belongs to is just going to give us a single foreign key and that right is reflected in the name and so now we can associate users and messages again but we can do it in this way basically now these users have a messages property that can take in messages so if we were to assign some variables here let's let Sam and Ryan equal the users and then for these let's let them just be these local variables message one message to message three and message four and we could pass these in to these create statements but we can also use you know other api's from Mirage like update which lets us update attributes so we can say let's update Sam's messages to be an array of messages and he'll have message to and message for and Ryan will have message one and message three so if we save that we can see we're still able to you know work with these models but under the hood Mirage is keeping these IDs in sync for us but now we do have this relationship encoded you know the messages kind of are on their own but if we wanted to query for all the messages we can go ahead and get our users and we can see from our client you know Sam has messages two and four in that way again if I were to query the messages I could stitch these together and say okay these are Sam's two messages right and we also have some behavior here where in the same way we can keep the the foreign keys in sync through these encoded relationships so Sam has messages 2 & 4 over here and so if we were to send a delete request to messages 2 we see the message ID is updated here and it's removed from the table so in the same way that the belongs to relationship works the has many also keeps things in sync and it's just another way to encode these relationships now one thing I do want to point out is if you do have experience writing servers or storing data and databases this might look a little strange to you because most database systems and back-end server systems don't actually support arrays of foreign keys like this they will have a notion of has many and belongs to but under the hood at the database level foreign keys will always just be individual keys and there's a reason for that but mirage supports this because mirage is kind of an abstraction around this and there's actually situations where even if the database is only a restoring foreign keys your server will expose server resources with an array like this just like we saw here so if we get all the users let's send a get request to users these come over as an array so mirage is really more about representing how server resources are exposed and the database is done in this way that has many side of the relationship just to keep it as simple as possible it ends up working exactly the same it's just a slight abstraction and so if you are confused about that you might find that most systems only ever support individual keys even if they end up giving you the same exact thing so this is just a little bit of a higher-level way to think about it and it makes it kind of easy you know like if this is actually how our server behaves for whatever reason like if you know in this case messages would probably belong to a user because that makes sense and we already said that it doesn't really make sense for a message to exist in our system without a key to a user it just does really make sense but there's lots of models and situations where it does make sense to have a record exists that may or may not point to something so in that case you might want the relationship to be on that has many side and this is the way your server would expose it and so with Mirage it's very easy to model that we just go ahead and add the new relationship and put as many and that's all there is to it Mirage is gonna keep these foreign keys and sink for us okay so we have has many we have belongs to you might be asking well why not both right so let's go over to exercise 7 and talk about the one-to-many relationship so so far we've only seen relationships on one side of the model or the other of the relationship and the other model is basically not even aware that it points to the other resource right it's just a one-way relationship well often you want relationships that are bi-directional so you want the relationship to be on both sides or for both sides of the relationship to be aware of the other and for those two things to stay in sync so let's see what that would look like if we come over to exercise 7 here what we're gonna do is import belongs to and has many and we're just going to say that a user has many messages and a message belongs to a user so if we save that come back look at our database will see our familiar message IDs column was added to our users and our user ID was added to our messages so now back in the code than here in seeds let's do what we did before let's am equals let Ryan equals and now when we're creating a message we can pass in a user because we've defined that as a relationship so user is Ryan save that and come take a look and now if we see Ryan has a message IDs column that has one in it and the first message is associated with Ryan so these two things are being kept in sync with each other even though we're just passing in the user right here so let's finish this user Sam user Ryan and user Sam and now these look just like they did before even though we were only really modifying the side so this is another example this is a bi-directional relationship Mirage is basically inferring that these two things should be kept in sync with each other and so if we were to go ahead and say delete messages slash one now we come back here and this is updated as well that key is updated so this is kind of the simplest example of a bi-directional relationship and again we saw how these were being kept in sync now you can make these two associations not represent the same relationship by saying inverse as null but in this case we actually want this so again why are we actually doing this in the first place well if we have this conversation going on does it make sense that if this message is deleted then it should be updated on this side well yeah Sam's messages have changed if one of his messages was deleted so this is kind of where you start to think about domain modeling from the sense of what is true about the logic of your system what is factual true about the domain of your system and that's why it's called domain modeling because there's really no right answer when it comes to a lot of these questions it has to do with the logic of your specific application and that can change over time which has implications for how you model your domain but when we're talking about domain modeling and these structures the question of which relationship to use is largely driven by what is true about your system so in this case you know a bi-directional relationship we can see exactly how Mirage is keeping these two things in sync and conceptually they represent the same relationship okay so over an exercise eight you know we've talked about one way belongs to relationships one way has many relationships and now we have a bi-directional one-to-many relationship and the question is well when do we want to use each one of these relationship types when does it make sense for two sides of the relationship to be kept in sync or they're only to be one model that has a foreign key and a lot of this again is driven by the facts and the logic of your domain but it's also driven by your UI and your clients and how they need to access data from your systems and so you know with our current set up where we have this one-to-many relationship here we have a user that has many messages and a message with a belongs to pointing back to the user we can see our data here and we know that our clients have the power to go ahead and render all the messages this conversation in some UI right so if we fetch all the messages there we see our hypothetical ember app could see all this data and it's it knows the user ID for each message and it can go ahead and grab all the users and it can stitch these together and render the UI right but you know this is kind of cumbersome and if we always made our front-end developers do this you know or we're doing this as a foreign developers it might not be the funnest part of our job right we have to kind of stitch these two pieces together and really we just want an easier way to make this request you know what if the first request to messages comes back but then for whatever reason the second request kind of times out we put our UI in a weird state ideally we'd want to be able to get all this data in one single request and kind of the first way that server-side system solve this problems was by using server-side includes so we can do this in Mirage with our serializer layer so over here in exercise 8 we see we have a serializer so let's come back to our code open up exercise 8 and right here so far we've just been using a single serializer for our whole application this is called the rest serializer and the serializer layer mirage or in other back-end systems really has to do with the format of the data so the shape of this data the fact that this is an array messages isn't the route here you know there's all sorts of different formats but we're just using this kind of standard one and so what we can do is actually define model specific serializers to alter the behavior of how those models are serialized before they're sent up to the client so in our case what do we want to do well we kind of would like it when we fetch the messages instead of just getting the user ID if we could also get the user record in this payload the way we can do that is by defining a message serializer it'll be rest serializer but we'll call that extend on it and then we can define an include key here which is an array of relationships to include from our server and because our message model here has a user relationship we can say alright go ahead and include the user so now if we go back to our inspector and fetch all the messages here we see just like before all the messages but if we scroll down we see a new key on our payload called users and it has a user's right there so just in one request we now have everything we need to render the conversation so that's pretty cool the client is able to fetch this whole graph of data in one request and looking at this you know it really looks a lot like our database right we kind of have these two root keys that represent our database table names and we have all this data here now by default the way we've configured this what we're getting is called side loaded data and that just refers to the fact that the main resource were requesting the list of all messages is here and then the included resources are kind of side loaded literally just they're loaded alongside the main resource here in this JSON object and this produces what's called normalized data and so normalized data really is just a fancy way of saying data that has no duplicated values in it so if we look at our database we can see that like we were saying kind of the beginning this ID field here it represents identity and what is identity mean it means this is the globally single place where this resource exists is defined it's not duplicated anywhere else so if I asked you what's the name of the user - I can look right here and see it's Ryan and it's not gonna exist anywhere else in the system and same with the text same with all these records and this is typically again how databases store data they are normalized so that there's no duplication you can think of again the database is really the root state of our entire web app and everything kind of flares out from that and so it's important when you're talking about the source of truth or the root state for anything you have no duplication and it's just like when we're writing ember UI code right if you're writing computer properties or you're writing other pieces that derive from root state you really want to identify what the smallest amount of root state is and derive everything else from that because when you duplicate data you run the risk that the two duplicated pieces kind of fall out of sync right so interestingly enough serializers actually have another option called embed so if we were to set embed to true let's take a look at how this affects our query so we'll go ahead and send a get request to messages and now we have the messages key here but if we scroll down we don't see the user and that's because the user has now become embedded within each message and so instead of just user pointing to an ID we actually embed the user like this now you may have worked with API it's like this before because it's a pretty common way to make it a little bit easier for the front-end developer to just get what they need and so you can imagine if we were rendering this conversation you know now we don't have to do any work about stitching all this data together on the client we can just loop over this array and for each object here render the text and then render user name and we're basically good to go right now question for you is this data normalized well I see name of Ryan right here and if I scroll down I see a name of Ryan right here so this payload is denormalized embedding often results in denormalized payloads so again there's kind of a trade-off here on the one hand if you're working in a client environment that doesn't have kind of the capabilities to stitch together all this data like ember data does you know ember data expects normalized data so that it can create this graph of relationships on the client and let you traverse that graph but other times if you don't have an API like this or you don't have a tool like ember data this would be much simpler in some sense to just make this request loop over these messages and render directly to the screen it's just that now you run the risk of you know editing this name and not having it reflect everywhere in your UI so if you're rendering Ryan in all these places right here and then you add some option to change the name well if you change it in one place you have to make sure to change it everywhere right so this is this is like a fundamental question of when does it make sense to work with denormalize data and to kind of derive data from root state versus sending over this the most highest fidelity version of the database data in the normalized way so client can understand where everything comes from and different situations sometimes call for different answers to that question another kind of back-end term you might hear thrown around conversations like this is called a materialized view and so really all of our responses have been materialized two views of the database but this one definitely is again because it is denormalized it's really looking at our database state it's a view of that state transformed in some way you might call here that called a materialized view now sometimes in back-end systems the query needed to power a front-end is so different from how it's stored ultimately in the database that back-end systems will materialize a view a very specific view of the database data and store that materialized view in another database table that is ready to be queried by the client and so again this makes it way easier for the client they can just fetch the data already in the format that they need for their particular page but you can see as soon as you do this you don't have to deal with this notion of stale data and caching because this materialized view is materialized from the root State so if the root state changes you need to make sure to regenerate that materialized view it sounds a lot like what we do when we query data with with ember apps right so let's go on to exercise 9 and you're going to get a little bit of practice time with include and embed so if we go back to the readme here and we go down to exercise 9 let's say instead of rendering the conversation we are in this hypothetical slack app that we're building we want to build a screen you know for maybe the current user where I can click on my profile and I can just see all my messages so I can just see all the messages that I've ever written so how might we you know expose our resources and how might the user query that data so go ahead and pause the video and give this a shot okay let's compare our work so we're over here in exercise nine and thinking about it kind of starting from the outside in is usually how I like to think about this if we're in nine what would I want to be able to do from the front-ends perspective to get this data so if we get all the users here we see we have two users Sam and Ryan let's say I'm on Sam's profile page and now I want to go ahead and include all the messages well we have the IDS right here so if we just included the messages relationship for a user we should be good to go so we should just be able to copy this define a serializer for the user and go ahead and include the messages right and if we haven't embed off and we get users slash one will go ahead and side load those messages and if we have embed on and we get user slash one then those will be embedded so this looks pretty easy to work with right we could just render the user out and then list over each over user dot messages and boom there we have all of our messages right here now this is an interesting question we set embed to true here is this payload normalized data or denormalized data well I don't see any duplication here right if we look at the database we have the name in one spot and all these messages have text in one spot so even though we used embed here because messages is it has many relationship there's actually no duplication going on here so you'll just see that sometimes different situations kind of produce different results but ultimately these are all kind of views or snapshots of the database as of a certain point in time now you might have noticed something interesting let's go ahead and turn embed off for the rest serializer for our user and if we get users one again what do we see here well we see that the messages are being side loaded the user Sam has messages two and four and if we take a look at two here this is message two with the text a man and the messages are also being serialized according to our message serializer which we defined right here and they're embedding their user now most server-side frameworks and Miraj as well have ways to avoid this basically to say whether or not a message a serialized message should include its user you know if the request was for users one or if the request was for messages so you can add more logic to make the server more intelligent about when it should include or not include a related resource but you can kind of already see here that this is a little difficult right our server is having to do its best to make a guess about what the payload should be for the client but really the client knows best about what data it needs to render a particular page if I'm rendering a list of users I just want those users I don't want their associated messages if I'm rendering a conversation in a channel I just want the messages and maybe include their users but if I'm rendering maybe I'm fetching these messages afterwards just for user one and I already have the user in my system and so I don't need to include those either so you have all these different pages that you're building in your UI each with their own specific data requirements this is one of the challenges with what we're calling server side includes here where the server decides whether to include related resources or not and so what you've seen in recent years is the ability for clients to have more control to specify on their own from the queering side whether or not to include related resources and that's what exercise 10 is all about so let's go over to exercise 10 and we're looking at fetching a graph again which is you know when we say a graph we're saying we we fetching graph of data which is nodes in a payload that point to each other which is ultimately what our database is right and with our rest serializer and includes that's what we were able to do to send a single graph of data that the client could kind of stitch together or iterate over so now we're talking about fetching a class a graph but having it be driven from the client side query and if you have used JSON API you kind of know where this is headed so really the only difference here we still have our one-to-many relationship we have our data but instead of using rest serializer we're using json api serializer and so if we look at our database here we can see users have many messages let's go ahead and get our users and again if you've never seen JSON API before it adds some more structure to our data so you'll see a little bit more structure and ceremony here but it's for good reason so our users response here is an array of these objects and the objects always have a type in an ID which kind of uniquely identify this resource and then we have this attributes key which tells us that there is a name of Sam so we can grab a Sam here at user slash one and what if we want to do that fetching where we fetch a graph well we can go ahead and say let's include the messages and just like that our JSON API back-end has included the relationships here and it tells us that your messages relationship has these two records and if we scroll down we'll see an included key there they are that's the full representation of that message resource and notice we didn't have to change anything in our server we are just using out-of-the-box JSON API serializer and you know the same thing works for messages we can get a message and we can go ahead and include the user of that message and this is all possible because JSON API is stricter in the sense of how it allows certain queries to come through and how it represents this data so this is pretty amazing how far you can get with this and a good question to ask here is whether this data is normalized or D normalized and in the case of JSON API it always produces normalized data so you'll see here that this is the message of ID 1 so we have again this notion of identity right here we have a pointer to our user which has this identity right here users clone 1 and then it's included right here and let's go back to the situation where we saw the denormalized data where we fetched the messages and then we include each messages user remember when we had embed off each one of those was embedded within the message and here we can see the only thing that each message gets as a pointer to a different user right so user to user 2 and user 1 and then down here and included we have the two users with their name so you won't see named Sam show up twice anywhere in this payload so this is completely normalized data which again is really just a transformation of the database with enough information for our client to piece everything together so that's kind of the difference between JSON API here is that JSON API always produces normalized data and that's because JSON API was really designed to get the data into a client-side identity map into a client-side library like an ember data that wants to know about all of the keys the primary keys and the foreign keys in order to stitch everything together and reform that graph of data on the client so this works really well especially when you have a tool like ember data or Orbitz something that expects a normalized payload to come over the wire but ultimately the higher level point here is putting more control in the clients hands right giving the client the ability to query a back-end and specify just what they need and that keeps our server more flexible in general right we we didn't have to change anything about these endpoints or about this serializer and our client has all these different ways to query the backend for the specific needs of the you I that they're building so does that sound familiar well let's look at exercise 11 and exercise 11 a little typo here is about fetching a graph with graph QL and so now that you kind of have an understanding of you know different ways to expose these server resources over the wire you should have a better understanding of the motivation for graph QL and this really takes this idea to the logical conclusion where it says we're actually gonna have one single end point if we scroll down and look at this there's one single end point here and it's completely generic and we actually have a query language that the user can make in order to fetch the data in exactly the right way they need for their particular use case so what does this look like well you'll see in our server here new server we don't have the serializer anymore and again we just have the one route but we still have our models and we still have our seat so again this is our kind of domain modeling part of this and this hasn't changed at all this is like what's true about our system data it's just how we're exposing it that's being driven by the UI in this particular choice of using graph QL now the other thing with graph QL here is that we have a schema and this is this is really similar to routes in a sense of its what we expose but it's a little bit different way of thinking about it we're in a similar way that we define our schema and our models we have this layer in graph QL to say all right our graph QL layer exposes a message type and a user type and a query for all the messages so what might working with this look like well we'll graph QL everything is a post request and our endpoint is graph QL but we need to actually send over a query here so this is going to look like this it's going to be a query and a graph QL query is actually a string and it's really its own micro language that looks a lot like JSON so this is kind of the outermost thing and if we look over here at our query type we see that we can query messages so we can just type in messages here and graph QL which is coming from the the official graphical package is pretty cool the integration here is pretty light with with Mirage but basically we are gonna get these helpful error messages the field must have a selection of subfields and so we can't just fetch all the messages and everything about them we need to be more specific and so we can do that like this so let's say we want to query the IDS for the messages I'll just zoom out so we have a little bit more room and there we see the response from our graph QL endpoint is just giving us basically the message ID fields from our message and so you can see the response really kind of matches the query and that's one of the big ideas behind graph QL now our messages also have you know a text property so we can say let's also get the text maybe we just want the text maybe that's all we care about and again nothing about our server has changed at all we're just able to query exactly what we want now if we look at the type that we've defined here it also has a user and so we can get our relationships just by specifying them like this and I think we'll get an error here where we need a sub selection as well because user is another type so we'll do the same thing and let's just grab the ID of each user and so now we're getting all of our messages with the ID but maybe we don't actually need the ID maybe we just need the name and so just like that we kind of have created this nice little payload it's an array of messages right and it has the text of the message and the name of each user so this is like really hyper optimized for whatever specific page we're rendering let's say we're rendering the conversation here we literally only have the data that we need for it now is this normalized or denormalized data well we can see that there's definitely duplication here so it's D normalized so graph QL is in some sense solving the same problem at Jason API is by giving the client more control about what it queries but it takes a different approach and says as far as normalization goes and D normalization goes that's up to you and you have the power to query something like this that's going to let you render exactly from this JSON payload what you need but we're not gonna help with basically normalizing the data and making sure that you keep things in sync that's kind of a layer above graph QL there's libraries that of course help with this but at the lower level you can see it's very easy to produce denormalized data so again back to these trade-offs sometimes it actually is really nice to just get a payload like this and render it and not worry about the normalization or not need a notion of identity on the client I mean we're not even including IDs here so there's really no notion of identity we could have different users that are both named Sam and we wouldn't be able to tell you know which one is ID one which one is ID three so this really takes that idea to the logical conclusion and I just wanted to show off this and you're welcome to play around with this kind of your own time pause the video play around with it because really it's just another way to solve it and obviously graph QL has become tremendously popular so it's not something you should just ignore you should understand the problems it solves and why certain companies and teams use it for certain problems and this is just one aspect of it there are other aspects of graph QL but I just thought it really fit nicely into this conversation right here in explaining how we can choose to expose our server resources to our clients all right let's go over to exercise 12 and we're going to circle back to a little bit more about domain modeling so here we have the user and message model and the one-to-many relationship just like before but we also have a new model called channel and down here we create three channels video and podcast and if we look at our database here we can see users messages and then channels is right here so the question is how might we associate users with channels well in slack a user Sam can join many channels right so a user has many channels I can be part of general and video but what about a channel a channel can also have many users right it doesn't make sense to just have a single key like user ID on here we would need we would need an array because both Sam and Ryan can join the general channel so we need a has many on this side as well so this is what's called a many-to-many relationship and we can do this if we come down here to exercise 12 just like you would expect so a user has many channels and channel has many users and now in our database maraj has added a channel IDs foreign key to our user side and a user IDs foreign key to our channel side so let's come down here and give these channels some users so users is an array of users let's say Sam and Ryan are both in the general channel and then in video users is just Sam and then in podcast users as Ryan and now you can see Sam has two channel IDs one and two which is general and video and the general channel has two user IDs one and two so again just because we passed in the users to the channels Mirage is keeping these foreign keys and sync and so now if I wanted to say well let's get users one and include their channels because we want to look at those well now we have all this data that we need right we've got the two pointers here and all of the data included so we could write out the channels list for a user or if I was in a channel let's get all the channels maybe we're gonna get channel 1 and we want to see all the users that are in this channel we can just say include the users here so this is a many-to-many relationship because you know the has many is on both sides of it for each model and we can see you know works out really well here for exposing all this data you know in whatever way we need and again just to see that Mirage will kind of preserve our referential integrity here if we were to go ahead and you know let's delete this let's say we have channels 3 which is the podcast Channel if we were to go ahead and delete that I think right now Ryan is part of channel 3 if we were to delete that from our system there we go this inverse relationship is updated so Mirage is preserving the integrity of our data for us but let's look at a common action that you might want to perform from the client to this data so I'm just going to go ahead and refresh this and right now Ryan is in channels 1 & 3 right general and podcast so we can get slash user slash 2 which is Ryan and include his channels there they are 1 & 3 now what if we wanted to write in the UI a way for Ryan to leave channels you know in slack and discord you can join and leave channels what would that look like as far as the request goes and the data that needs to go back and forth well if we look at our users table here you know what needs to happen is we need to either update the user Ryan here and remove three from the channel IDs or update the channel and remove two from the user IDs here right that's kind of what needs to happen in the database layer and so the way you can do this with JSON API is that we can grab this kind of snapshot of the Ryan user model here and we can send a patch request to user two right so this is how you update records using rest so if we were to paste this go ahead and close this out this is basically you know the current state of this user record so if we were to just send this over we see it doesn't change and if I were to change the name to ryan toronto send this over we see that that updates so you know patch the way patch or put works is that it overrides kind of everything in your back-end so you need to know enough about the current user in order to update it and so here we have the channel IDs here and right now Ryan belongs to channels 1 & 3 so let's say we wanted to remove him from the third channel the podcast Channel well we can just remove that from the list of foreign keys here this is really a foreign key in the language of JSON API and so if we say Ryan's only channel is channel 1 and then we send that over now the channel IDs update and also our podcast channel is now empty so that is what our client app would have to send over you know in order to remove Ryan from a channel but you know if you think of it's kind of a bummer to have to know all this information right just because of the way patch works and rest works you're really transferring the state of a resource you know from client to server or from server to client so the client has to know all the channels here that I that the user already belongs to just to remove one channel so we have to know that Ryan is in channel 1 and channel 3 just to remove them from channel 3 and you can imagine in a big system you know if we had a ton of keys here just to remove like if this was all these keys right and we needed to know them all just in order to remove him from one I mean that's not really a sustainable thing that doesn't really make our lives easy as front-end developers you know it would be much easier if we could just send a request that's focused on you know we have user to here and we want to remove them from channel 3 that's all we need to know in order to make that action occur and so you know this feels like there's not really a good way to do this with how we've modeled this domain with these three tables and these relationships way we have it there's no you know there's no combination of verb and noun here that makes that very easy so what happens a lot of times you'll see teams do and you'll reach for in situations like this is to basically start making up new actions and verbs that your server exposes so if you've ever worked on a problem like this and you say you know I've really just one endpoint where I can just remove a user from a channel so maybe I have a user slash two and I have like a remove channel action and maybe I just send over something like I don't know channel ID 3 something like this right who knows we just get to make up stuff and so you know this feels like it makes my life so much easier right because I've got my user - I just need to remove the channel and now I don't need to know everything about the user just to do that I can make this very simple request and it seems a lot better right you know other things that you might have run into with this like maybe you need to subscribe a user to something so you do something like user / 1 / subscribe and you just say all right let's the client app make this request and then will maybe send off an email something that doesn't really fit into the normal crud operations that we've been talking about here or maybe you have like a posts that you want to just publish you want to click a button and publish a post that's already that already exists and you need to like send it out to some service and send an email or send a notification so you just say all right what if we just had like a publish endpoint that we could just hit and trigger all these actions right so the problem with this approach is that you're basically going outside the boundaries here where you're going outside the lines that this entire architecture is based off of and like we said at the beginning you know you can do anything here because every time we make an app we basically are writing a lot of this stuff from scratch and so it's very easy to reach for this kind of thing because it seems like the simplest solution to the problem but there's actually a better solution and the solution here is to think how could we model our domain in a way that would let us achieve what we're trying to achieve just using these verbs and resources and keeping our code super conventional you know up to this point we've been able to use all these short hands because we're really abiding by the principles of rest and it's been very clean so far we haven't had to use any escape hatches so how might we do that here and the way you do that is by creating a new resource that represents the relationship that you're trying to change so what would that look like here well our our user has many channels and our channel has many users what represents a user in a channel belonging to each other right well that would be a membership a membership is the noun that reflects the notion of this relationship right so far the relationships that we've described and defined kind of make sense on their own but this many-to-many relationship is really significant enough in our domain and in our UI that it really deserves its own name and so times when you run in these problems you start thinking about this and you say oh yeah that's a membership that actually makes a lot of sense right it makes sense to talk about a membership as a separate thing apart from the user and the channel and you know you can imagine like maybe there's private memberships maybe maybe certain people can belong to different channels based on authentication rules so once you get to this point giving this thing a name usually opens all sorts of possibilities for ways to improve the domain modeling and how you expose that data it to the front end so what would it look like if instead of making this a many-to-many relationship here we actually introduced an entirely new model just for this purpose well let's go over to exercise 13 we'll see that we have our channel set up with our existing many-to-many relationship and what we're gonna do is just come over here to 13 and we're gonna create a new model and it's gonna be called a membership and what is a membership it really joins a channel and a user and so a membership is going to have a user that's a single user so it belongs to and it's gonna have a channel that is a single channel so it's belongs to and we'll go ahead and get rid of this many-to-many relationship that we had defined earlier so now if we come and look at our data user has no notion of channels and channel has no notion of users so it looks like we have some old data creation logic here so down here instead of updating our channels passing in those models we'll just get rid of that so now our channels have no notion of users users have no notion of channels but membership is really going to be the record to store that relationship so instead of updating like we had before the user's property on a channel to create this relationship well now we can just call server dot create membership and let's go ahead and create a membership between a user of Sam and a channel of general again our user still has no notion of channels in the database layer same with channels and users but a membership now exists between user 1 and channel 1 let's create the other memberships Ryan in general Sam and video and Ryan and podcast and there they are ok so you might be thinking well that's pretty cool we have this separate thing here but if I want to my channels list like the first thing that happens in slack when I sign in is I see all my channels now when I go to user slash 1 there is Sam but how can I possibly query for my channels because the only relationship that Sam has are these messages well we can create a pointer to this relationship right here we know a membership belongs to a user so this is really another one-to-many relationship so if we come to our user and say you know what the user actually has many memberships right and we know that because we know the membership points back to the user and so kind of from the storage perspective right there there's all that data and again this is really just boils down to another one to many relationship between users and memberships and in the same way a channel has many memberships right that's what we started this off saying is that a channel doesn't just point to a single user it has many and now we're representing that relationship via memberships so we can say a channel also has many memberships okay and now when it comes to querying let's go ahead and get Sam and if we look at our schema over here Sam has messages so we can we can include his messages if we wanted to but if we just wanted to get his channels how would we do that we can get the memberships and there are the memberships which is not really helpful in terms of showing us the channel names but with JSON API we can actually traverse this graph using dot syntax and every membership has a channel so now when we hit enter we get the membership the membership points to a channel and there we go Sam belongs to general and video just with one query just by traversing our model like this so that's pretty cool but earlier we were able to get all of Sam's channels the hard part was updating them so let's say that we wanted to remove Sam from the general channel right we see down here Sam is in both the general channel and the video channel well now what we're going to be able to do is get a hold of the membership that represents this so general channel is ID 1 so channel ID 1 and Sam is user ID 1 so this first record this first membership row right here represents Sam belonging to general so how do we remove Sam from a channel that's easy we just delete memberships 1 oh we forgot to expose the resource down here so let's come down to routes resource membership and this is important right membership is a first class concept in our system that we're now exposing to the outside world and again there's that membership Sam belonging to general go ahead and send a delete request to slash memberships slash Wharton we do that and so if we were to wreak weari for users one which is Sam and include all of his memberships channel here we go we just see video and so this hopefully really shows off the power to you of getting the domain model right because this problem that seems so intractable before how can we stay inside of these normal crud operations without adding something like you know a slash add or remove channel action to our API which we know feels bad but we don't know how else to do it just by naming a new resource and getting the modeling right between these foreign keys and relationships in a way that makes sense for our domain means that now we can create relationships and delete them in a very first-class way in a way that's going to feel right whether we're using ember data and calling store dot create record and find record all of these kinds of things it just makes this whole graph of data that exists in our database much easier to query and modify from our client and so you know just like before we were talking something temp it can be tempting to say you know we want like a subscribe action but you know subscribe is not a verb so we have this same problem if you find yourself reaching for this you probably need a new relationship so what would this relationship be well maybe it would be a subscription so if you have a subscription that points to a user now you can create it and that would be the verb that would be the post verb a normal crud operation on this new resource and same with the posts / 1 / publish what would that be well that would actually be a publication so now if your app needs to publish a post all you need to do is create a publication right so this is an extremely powerful concept that really kind of sheds light on this whole question of domain modeling and how important it is to get your domain model right if you look at the readme you'll see at the bottom here one of these talks is from DHH and it's called resources on Rails and it really does a good job of making this point so I highly encourage you to check this one out all right let's look at the last exercise which is just going to give you some practice with this concept let's say that we want users to be able to be friends with each other so if we come over and look at 14 here we're back to just users and messages here and let's come over to our code and look at 14 and one thing you need to know for this exercise is that so far we've just used these has many and belongs to kind of by themselves but let's say we wanted to add a friends relationship to this user model and a user can have many friends hopefully you have more than one friend so this is probably gonna be it has many right well by default this is going to look for a model called friend we don't have a model called friend and we don't necessarily want to want to add one right because a friend is just a person and we already have this user model so in Mirage you can say you know this friend's relationship is actually a user model just like this so far we haven't had to do that but here's how you do it and so we can create a many relationship right here come and look at our database there's our friend IDs let's come create some more people will create yehuda and tom and then let's go ahead and say Sam update friends are there all my friends Ryan you hid in Tom right and so now we have a many relationship here between a user and their friends so that's how you would create a relationship that has it has a name that's different from the actual model name of the underlying model so the exercise here that was just what we what I just did is a very simple kind of has many version of this but you know we just saw some problems with has many's when it comes time to update them from the client it's much easier to work with this new record that represents the relationship which I forgot to mention but you'll you'll sometimes hear it's called the join record and so up here we just went from a many-to-many with this join record by making a membership and now we have a many-to-many without a join record and the goal is for you to make a join record and be able to do the same kinds of things that we were doing to membership so if i became your friend what would that look like from the perspective of the query that the client had to make or if someone unfriended you what would that look like when you have that new join record so this one's a little bit tougher to last one but I encourage you to pause the video give it a shot and make sure you kind of you know it can be a little confusing when you first learn this stuff but it's really important to give yourself enough time to try to think about it because when you identify that new resource it makes a lot of things click so pause the video now and give this a shot all right let's see how we might approach this problem so again we have the friend IDs here with the many-to-many and so if I got user slash one include my friends well there they are but if I wanted you know if you hooda was my friend anymore we'd have to do that same rigmarole that we did before I'd have to copy all this and we have to send a post request with all these IDs except for ID three right and so instead we want to create that new resource that represents the relationship basically whenever you start performing crud operations on relationships it's sometimes a really good sign that you need a new resource right and so what might this look like well instead of being a friends here what if we had a friendship and a friendship is a relationship from someone so it belongs to a user and to someone else right and you know there's a lot of ways to model data when it comes to this stuff you know we've really been covering the core fundamentals the basics domain modeling is a really complex topic and really experienced people will disagree about how best to model something for their project but this is just one approach you could take and so now we have a friendship right here and a user has many friendships and this way we can get a user and all their friendships right and if we come back and look at the app we're actually gonna see a white screen if we look at the console we'll see an error it says Mirage you're trying to create a user model and you passed in this Oh it's because we need to update our seeding logic here so this is no longer valid let's just delete that and so now we have a friendships table so instead of doing this where we update Sam's friends let's go ahead and create it's kind of funny language let's go ahead and create a friendship from Sam to Brian I think he's still my friend so we'll do that and now we're going to hit another error and this is kind of just starting to get into how some of this gets complex right but it says mirage says the friendship model has multiple possible inverse associations for the user dot friendships association and so what that means is that you know mirage is trying to keep these in sync and it says hey friendships right here has many and over here we've got two foreign keys that point back to my user so I need to know which one of these I needed to track which one I need to keep in sync with so we can tell it by doing inverse is the from case and so now Mirage will keep the friendship IDs key on the user and sync with the from ID key on the actual friendship and so now we should be able close that console and look at this again and so now Sam has the friendship IDs right there and over in friendships we see a from ID and a to ID right there so we can go ahead and create all the friendships from Sam to yahuda Sam to Tom I guess I'm just getting lonely because it's coronavirus you know and there we go we see all the Sam's friendships so now our client would be able to say let's go ahead and query Sam and include all his friendships and each friendship we also want to include the two and so now we can look at what's included right here there are the friendships and there are the friends the actual users and if you could I didn't want to be my friend anymore how did we do that well you who does 3m1 find that friendship one two three so this is friendship two and of course you don't have to look all these keys up presumably your UI would be able to present this in a much simpler way but just again to make sure you really can just feel comfortable understanding how this data is stored at the database level now to remove that friendship all we got to do is send a delete request to friendships / to and of course we need to expose this friendship so friendship between us friendships to delete send that off to ou for that one's gone and over in users that's been updated as well so again just an example of how powerful this idea is of coming up with new nouns that really represent the relationships in your system it's really an expansive idea that can really clean up your code keep your server code really clean but also keep your client-side data fetching and data manipulation code clean because look we're just sending a delete request to a resource that's all there is to it so no need to make all these new verbs and this is really the trick to staying in the lines kind of as long as you can so hopefully that was a good tour of the fundamentals of modeling your data hopefully you you appreciate and understand a little bit more about how important it is to get this right and I'm sure you know if you've worked in on an SP a like a big ember app that is data-driven youyou understand at an intuitive level right how much the data modeling can affect your productivity and your team's ability to really think clearly about all this stuff so the question is you know how do you get better at this at choosing good domain models at encoding the logic and facts about your system into the model in such a way that makes it hard for folks to kind of screw it up when they're either fetching or manipulating data really the best thing to do is practice and over time you'll develop an intuition you'll say you know what we don't need a subscribe action there's actually a subscription there or really oh this this is really a new relationship that should be pulled out and what's the best name for this how can we talk about this in a first-class way you know a lot of times just the way people talk maybe not even non-technical people at your company maybe they talk about subscriptions and memberships and then you think oh okay that actually would make sense as a table and so far that notion has just been embedded deep in our database in our server resources we haven't really identified it as cleanly but this is something that people think about in the first-class way so let's pull it out give it its own name figure out the best way to relate it to other models in our system and it can have a pre dramatic effect on how clear your code is and how easy it is to to understand and manipulate the data in your system so hopefully you learned a thing or two there if you have any questions we're going to be in the discord channel you can also reach us on email really appreciate your time and thanks for watching
Info
Channel: Sam Selikoff
Views: 2,084
Rating: 5 out of 5
Keywords:
Id: kKV7zQRZPSY
Channel Id: undefined
Length: 94min 27sec (5667 seconds)
Published: Mon Mar 30 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.