Domain Modeling for Humans and AI

Temperature Range Value Object

Domain Modeling for Humans and AI

Lesson Description

The "Temperature Range Value Object" Lesson is part of the full, Domain Modeling for Humans and AI course featured in this preview video. Here's what you'd learn in this lesson:

Mike walks through creating a temperature value object and monthly range entity in the backend. He covers defining columns, handling complex types, setting up relationships, and managing keys, data types, and metadata for proper functionality.

Preview
Close

Transcript from the "Temperature Range Value Object" Lesson

[00:00:00]
>> Mike North: Let's switch to our back-end here. So where we're going to go is in our server package. We're going to go into source entities. We've got a location here which matches what we already have described in our location. We kinda ended up undoing our change there. So this file hasn't been touched.

[00:00:27]
But we need a monthly temperature range and we'll need a temperature value object. So let's start from the bottom up. So we've got in values, we call this monthly temperature-range.ts. We'll grab our RGB color and we'll use that as our starting point. And what we can do here is say, monthly temperature range.

[00:01:00]
We want something that matches if we wanted to. We have to think about whether we want something that matches the representation in the types package. We have the option of persisting this in our database in a way that's a bit different from how it's exposed through our API, but I don't really see a need for that in this case.

[00:01:21]
Let's see if we can keep it simple. Now I'm running into this problem where the types package is not resolving here. Let's see. Import from peashoot types. Obviously you have to alias it. I was just saying the value is never read, that's fine. One last step here we have a single entry point for this peashoot types package.

[00:02:08]
If we were to click on this, you can see we export a bunch of stuff here and we haven't introduced monthly temperature range type js. If we hover over that, we can see this resolves and we also have to worry about the value objects temperature type js. We did this and then we did this one up here.

[00:02:37]
So this ensures that this package exports the files that we just created. Where we left off is if we look at the changes that we've made first in our types, we declared this monthly temperature range schema and we exported the type for it. We've got the month, the min, and the max.

[00:02:57]
We had a location here. I removed it for now and we're going to see why. And in the value objects, you can see we've got like, basically we've articulated this shape. Value is a number and the unit is Fahrenheit. And then we made sure to export these two newly created modules from our types package.

[00:03:21]
Now we're turning our focus back to the server side. And so what we've done here is. I've got an empty class here and we're getting ready to articulate this database entity. If we remember from the slide with going through type orm, we have to add entity to this.

[00:03:46]
And there are options you could pass here. You could say, I want to describe the table, the name of the table that'll be created in the database. But we're not going to. No need to get fancy here. Now it's time for us to define columns. I've actually borrowed our interface from that types package so that in this case we're going to keep this well aligned.

[00:04:13]
Like when this object is serialized into an API response, or when we're working with it in our business logic, or when we're storing it in the database, it's all the same representation for a simple object like this, at least when you first start working with it, you often start out this way.

[00:04:33]
Inevitably you make breaking changes internally because you need something about your database representation to change and you can separate these things. But that is what this represents. We're linking the API representation and our server representation of this concept. Just refreshing ourselves on what defines this. We've got month and min and max.

[00:05:00]
Here we go. We've got a column and month. We're going to use the definite assignment operator here because our orm will take care of making sure that this exists whenever we load it from the database. And just so if we didn't have this, you would see TypeScript complaining that, look, if you create a new instance of this thing, I have no guarantees that month is going to be populated with something.

[00:05:32]
This exclamation mark is basically telling TypeScript. It's telling the compiler there is some other process by which this will be populated. We can rely on that. When it comes to the type checking, assume this will get initialized and type orm is going to take care of that for us.

[00:05:53]
Then we have a min and a max. I've auto imported temperature from our types package. Remember we exported this type. I want to do the same thing with max. Min and max, and these both also need a definite assignment operator or, yeah, operator. And we're gonna add column to those as well.

[00:06:29]
Now, there is something special we're going to have to do here, because all we've stated here is a type of type orm. Sorry, we have a complex type here and all we've done is say max is this compound field. It's like this object. And SQLite, the database we're using here doesn't have a column type that matches exactly this.

[00:06:57]
So we have to do some work to see to make sure that the ORM knows how to persist this thing. And in order for that to work. We have to create the value object on the server side. It's going to look a lot like this. So I'm going to copy the contents of this file and we're going to go into this values folder and this is where we're going to create temperature.

[00:07:21]
And I'm just going to use this as the starting point. So just so you can see the file tree, it's in the source values temperature. And we'll just do this, we'll call this iTemperature. It's being used up, implements, and because this is a value object. Right? This is not going to have an id.

[00:08:03]
We're going to get rid of this. It's not an entity, this is a value object. And we see that we're incorrectly implementing this interface. So here we really, really want to keep our API representation and our server representation aligned. We need a number and we need a unit that's either C or F.

[00:08:27]
So we need value and we need unit. And it's going to be a string. But really we want temperature unit. We already defined this, right? We got that nice union type with the C or F. And this is coming right out of our Zod schema and it's sort of trickling through and we can get rid of this.

[00:08:52]
So now we have a temperature class. And what we're going to see is that when this gets persisted in the database, it'll sort of be embedded with its own columns and some prefixing so that it doesn't end up being sort of flattened in the column layout. But when we're working with this in memory, it's going to be this nice nested object and it just feels like a nice value object there.

[00:09:16]
So now we're going to grab this class and instead of referring to this temperature as just sort of like the type, we want to refer to the entity. And so be careful when you pick from these two. This up here is just the type information. This in value temperature, this is the class.

[00:09:36]
And one more last thing we have to do is this. And what we're doing with this here is we're saying, when you need to instantiate a type for this. When you're reading a row from a database, you need to create this temperature object that we just defined. This is the function you call to create that thing.

[00:10:08]
So now we have our monthly temperature range, we should be able to build. There it goes. Now we have one last thing we have to do, and that's associating with a location. We're going to do that with another annotation here from. Gosh, it's not belongs to. What the heck is is.

[00:10:55]
It's one. One to many to one, that's what they call it. Or no, sorry, one to many. One to many is what we want. So this is a one to many relationship between the monthly temperature range. Let me make sure location's coming from the right place. Should be the entity, not the types package that you're getting this from.

[00:11:27]
It needs to be definitely assigned like anything else you should see class, location, not to be confused with window location or anything like that. We need arguments here. This is where we can look at some other examples that exist here. Let's see. Plant has one of these I think.

[00:11:54]
Look at this. We've got a many to one. So we need something to instantiate the record as we had before and then some way to fetch how this relationship works. Here we go back to monthly temp location, we've already imported it and then the last thing we need is given a location like what's the other side of this relationship and we can wire that up on the other side.

[00:12:30]
So for now let's just say it's location, monthly temps, something like that. Now this field doesn't exist yet. We're about to create it on the other side. So we're going to go over to location and say many to one. Actually I think this side is the one to many.

[00:12:56]
This is going to be monthly temps, monthly temperature range. We need definite assignment. We need to make sure this gets imported and this will need something like range location. So we've made this side happy. Just so you can see the two components here. This top one is create the record and then this one is walk the association.

[00:13:47]
Right, like how do I from a range get location and then on the other side we're going to need from a location how do I get the range? So we've got a one to many here. Sorry, this we're going to change to like a many to one, right?

[00:14:07]
So we've got the location and then this should be actually should be like this monthly temps we have many and on the location side we have one location going back. So what we've done in summary here, we've set up the has-many relationship. We have a value object of temperature.

[00:14:25]
We have this monthly temperature record and where we're getting really well positioned to incorporate this in the API contract for the temperature calculator. Let's just do one more build to make sure that this all works great. I'll make a git commit here. So we're going to call it DDD1 monthly monthly temperature ranges.

[00:15:28]
So if you want to pull down the DDD progress branch, you'll get all the code changes that I just made and it should work. The next thing we're going to do is turn our attention to getting that dropdown working with with the list of locations and then we can incorporate this sort of temperature checking logic.

[00:15:55]
So if you try to run the app, if you run NPM run dev in the project, you'll see that entity, mentor. Metadata for location monthly temps was not found. One last thing we need to do here. There is sort of the entry point for the way our server deals with different entities in our database and that is in our data source TS file.

[00:16:24]
And in here you're going to see we've got a list of entities important to know. I think this is the full set of things that need to be pulled from different tables and it'll include the value objects as well. So we're going to add monthly temperature range, making sure we pick the correct one here and temperature format.

[00:16:53]
It hit save and this error should go away unless we've. We need a primary key here. I have a good solution for that. What we can do is extend from this base class. So if you look at some of these other types, you can see I extend peashoot entity plant and I have this constructor.

[00:17:14]
This will take care of our. Our primary key for us. The conventional way to do this would be to add one more column to monthly temperature range like this id and we would say this is auto incrementing primary generated column. And you could say type is a UUID or something like that.

[00:17:51]
Or I think you can just do uuid, yep. But we have a base class that'll take care of this for us and give us a nice prefix for this URL. This alone should be enough to make this work here. But let's borrow this concept that we see elsewhere in our code base.

[00:18:26]
Something's weird here, but, We're gonna implement it. We're going to fix our imports here and we should. So I just deleted this interface here just to keep things going smooth. But something odd where it's treating this as both the class and an interface. And still I think this is basically like a little caching error that I have locally.

[00:19:10]
You shouldn't have to do that step, but we'll factor that back in. I'm going to just copy what we have from plant. And what we're doing here is we're basically picking a nice ID prefix for this. So we're gonna just call it mtr for monthly temperature range. And looks like that didn't quite make it happy yet.

[00:19:35]
Data type object and monthly temperature range unit is not supported by the SQLITE database. So monthly temperature range unit. Unit belongs on temperature. Let's check that out and see if that makes sense. So we've got entities, we've got values temperature unit. We can call this text. And now it starts working.

[00:20:12]
So temperature range unit. It's basically saying, I don't have a column type for this union type of C or F. We can just say, look, store this as a string. Great. Now we could create these temperature ranges and store them in our database, which is exactly what we're going to do next.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now