# Stop Passing Primitives Everywhere (Use Value Objects)

## Метаданные

- **Канал:** ArjanCodes
- **YouTube:** https://www.youtube.com/watch?v=CWYwz3iV1g0
- **Просмотры:** 60,428

## Описание

🧱 Build software that lasts. Join the Software Design Mastery waiting list → https://arjan.codes/mastery.

In this video, I show how to use the Value Object pattern in Python to model prices, percentages, and email addresses properly. I’ll enforce invariants once, eliminate ambiguity (is 0.2 equal to 20%?), and make invalid states hard to represent, without adding unnecessary complexity.

🔥 GitHub Repository: https://git.arjan.codes/2026/value.

🎓 ArjanCodes Courses: https://www.arjancodes.com/courses.

💬 Join my Discord server: https://discord.arjan.codes.

⌨️ Keyboard I’m using: https://amzn.to/49YM97v.

🔖 Chapters:
0:00 Intro
1:44 Primitive Obsession (The Actual Problem)
2:30 The “Before” Example (Looks Fine, Isn’t)
3:10 What a Value Object Actually Is
3:47 Price as a Value Object (Behaves Like a Float)
5:39 Percentage as a Value Object (Kills Ambiguity)
7:27 Keep Business Logic as a Function
10:27 Second Example: EmailAddress as a Dataclass Value Object
12:03 Quick Mention: Pydantic
12:33 Why Enums Aren’t Value Objects
13:08 Final Thoughts

#arjancodes #softwaredesign #python

## Содержание

### [0:00](https://www.youtube.com/watch?v=CWYwz3iV1g0) Intro

Here's an example of code that deals with prices and discounts. As you can see, I'm using a primitive type here to represent those things, floating points in this case. Now, the problem is that these things can't be just any value you like. A discount has to be between zero and one because it's a percentage. Price has to be non negative. And that's why we have this validation code here in the apply discount function. Now, the problem is that if you add more functionality to this code, you add extra functions that do other things with prices and discounts or percentages, you're going to end up with this type of validation code all over the place. Also, because these primitive types don't really have any extra information, it's really easy to make mistakes. Is a value of 0. 2% or 0. 2% discount? Is a price already rounded to two decimals or not? In what unit is it expressed? Is it cents or uh the currency unit? We simply don't know by looking only at these primitive types. So today I want to show you one of the most underrated design patterns in Python, the value object pattern. And this is really simple but still very useful. And you know what's also useful? Well, I'm working on a brand new program called Software Design Mastery. This is not just another online course. It's a full program that teaches you the fundamentals. how to think about design, refactoring, modeling real world problems in Python. This program goes way beyond what I teach here on YouTube. If you want to become really good at software design, get access early and you don't want to miss this, join the waiting list for free at ar. codes/mastery. The link is also in the description.

### [1:44](https://www.youtube.com/watch?v=CWYwz3iV1g0&t=104s) Primitive Obsession (The Actual Problem)

Now, like I said, in this particular example, I'm using primitive types to represent things like prices and discounts. If you do that too much, that's also called primitive obsession, where all your domain concepts are represented as primitives. Prices as floats, discounts as float, email addresses as strings. Now, the problem is not that primitive types like strings or floats are bad per se. The problem is that these primitive types don't carry any meaning. They don't enforce any rules and they don't explain any intent. So every function that you write by using these primitive types ends up defensively validating all the inputs and inevitably uh you're going to forget one of these validations and then you're going to introduce bugs in your code. In

### [2:30](https://www.youtube.com/watch?v=CWYwz3iV1g0&t=150s) The “Before” Example (Looks Fine, Isn’t)

this particular example like I said at the start of the video there's assumptions everywhere like discount is supposed to be between zero and one but we can't understand that from the signature of the function. Uh we also assume that price is non- negative and floating point inaccuracies which can be a problem with prices. Well, we're not really dealing with them at all. And the usual fix is to just add checks everywhere which well it works but it doesn't really scale because you will repeat the same rules. The behavior depends on developer discipline. Rules become kind of tribal knowledge. And this is exactly what value objects are meant to fix. Now a value object that

### [3:10](https://www.youtube.com/watch?v=CWYwz3iV1g0&t=190s) What a Value Object Actually Is

actually comes from domain driven design is an object that is typically immutable and you compare it by value not by identity and the most important thing is that it's responsible for enforcing its own invariance. So the core idea is that when you have value objects you validate once when you create it and then you can trust the value everywhere else. And basically uh what this will do for you is that it's going to make it much harder or impossible even to represent invalid states like a negative price or a discount that's more than 100%. So how

### [3:47](https://www.youtube.com/watch?v=CWYwz3iV1g0&t=227s) Price as a Value Object (Behaves Like a Float)

would you represent a value object in Python? Well, one thing that you can do is use a data class for that. And let's create a class called price. And one thing that we can do, we want value objects to be immutable. So we can actually make it frozen like so. And then price is going to have a value which will be a floating point. And then what we can do in our post init method is that we actually validate the value. So we could check that if self do value is negative then we're going to raise a value error price must be non negative like so. And now we can do instead of passing a float here we can pass a price object. Of course, this needs to be price dot value. And this check we no longer need here because that's already encoded into the price object. And since it's frozen, we won't be able to change the value after the fact. So here, what I'll do is create a price object. And then I can do the same thing in the other examples. So and as you can see here is a third example where the price is actually negative. So now what happens if I run this code you see that actually we get this value error that price is non- negative and that is handled by our frozen data class and I don't need to put the validation logic here anymore. And of course I can

### [5:39](https://www.youtube.com/watch?v=CWYwz3iV1g0&t=339s) Percentage as a Value Object (Kills Ambiguity)

do the same thing for percentages. So let's create a class percentage. This will also have a value and here we can put the validation for percentages again in a post init method. Let's copy over this code. And of course this is going to be self value like so. And now instead of passing a float here we can pass the percentage. We don't need validation anymore. dot value and then we specify the percentage like so. And in this particular example, we have a percentage that is way too big because it's more than one. So when I run this you see that we get a value error and probably we need to change the error itself by saying that a percentage must be between zero and one. The nice thing about this approach is that we have nonmutable values and validation happens when we create them and then wherever we use these things we can be sure that they actually have the value that we expect. And in apply discount, you also see that the signature is now clearer. We know that we expect a price and we also know that this is supposed to be a percentage. So you don't have to worry about that anymore. And everywhere else you use these things, well, you can also assume that these invariants actually are held. Now, if you find the

### [7:27](https://www.youtube.com/watch?v=CWYwz3iV1g0&t=447s) Keep Business Logic as a Function

data class approach a bit annoying because you have to access it by doing dot value every time, there's a couple of things you can do to fix that in Python. One is that you define operations here like adding, multiplication etc. And then you can treat this more or less like a number. Another thing you can do is actually not use a data class but inherit from float. Now float itself is immutable. So this is still an immutable value which is nice. So then we can remove this and then we can implement new which is what is called when we call price with parenthesis. And there what we can do is so we get the value let's say that's of type any let's turn it into a float and then we check whether that value is negative and if it is we raise the value error and then finally we're going to return actually this should be self then we're going to return super call of new and we're going to pass the class and the value like so. This gives me a price object that behaves more or less like a float. And remember floats in Python are objects. Float is a class. So we can do this. And now what I can do is instead of doing price value, I can simply do the multiplication because that's defined for floatingoint values. And now what I can do here is I don't have to change anything here, but here it becomes a lot easier to deal with. On the other hand, you might think this is kind of hacky and you prefer the data class approach. Let me know which one of these you prefer in the comments. So if you want to do the same thing for percentages, then it would look something like this. And then I can also remove the dot value here. And then we simply have this check right here that the percentage needs to be between zero and one. Now, because it's a class, maybe you want to do some extra thing. Let's say you also want to be able to get a percentage from a value between zero and 100. That might be useful, right? Well, in that case, that's nice. You can actually define a class method that does that. And then of course we have to divide the value by 100 and then return that. So now what you can do is in this case we wanted to use a percentage of 20. I can do from percent and then that is going to work just fine. So when I run this now it actually works as expected. So that's another nice thing about the value objects that you have a bit more flexibility in how you create them. And by the way, if you're enjoying this kind of design focused Python content, make sure you like this video, subscribe to my channel. It really helps me out.

### [10:27](https://www.youtube.com/watch?v=CWYwz3iV1g0&t=627s) Second Example: EmailAddress as a Dataclass Value Object

Final example that I want to show you is with email addresses, which is also a really good use case of value objects. Now, not all value objects have to behave like primitives, right? We've seen that in the previous example. We could use a data class instead of a float. But for email addresses, this is also very useful. So for example here I have a regular expression that checks whether an email address is valid. Actually this is a hugely simplified regular expression. An actual regular expression for checking emails can be way and way more complex especially if you want to exclude or include certain domains. Uh but here I have a data class also frozen. So it's an immutable object where we have an email address that has a value and we have a post init method just like we had before where we do this check. So if the regular expression doesn't match the value, well, we're going to raise a value error. But then because we have a class, we can do other things like adding a property that gives us the domain. Or maybe you want to do other things with email addresses and that's now really easy to do. And when I run this, then you see it prints the domain as expected. But if I try to create an email address that is not valid then again of course we're going to get this value error. So this is the same pattern that we see before. There is an invariant which is defined by this regular expression behavior like extracting the domain lives with the data and we don't have to do this check every time we add an email address. So it's really nice solution. So few

### [12:03](https://www.youtube.com/watch?v=CWYwz3iV1g0&t=723s) Quick Mention: Pydantic

caveats. One thing is pideantic. You may think well if we have these value objects we could actually use pyantic to validate them right. Well yes you can do that. You can definitely implement value objects with pyantic but it also adds of course extra overhead. Uh and for pure domain concepts I actually like using data classes that are frozen or simple Python classes. It's a bit simpler, easier one less dependency to worry about but you definitely can if you want

### [12:33](https://www.youtube.com/watch?v=CWYwz3iV1g0&t=753s) Why Enums Aren’t Value Objects

to. Another thing you might wonder is uh is an enum a value object? So enums they represent a finite set of labels, right? You have different objects. That's not exactly what value objects are because value objects they have the valid values but with rules and invariants. So a value object should check that it's actually valid. An enum simply answers which option is this? And the value object is more about hey is this valid and how does it behave? But enums of course they can support value objects but they don't replace them. So in my

### [13:08](https://www.youtube.com/watch?v=CWYwz3iV1g0&t=788s) Final Thoughts

opinion value objects are really nice design improvement that you can make in Python. They help fight primitive obsession by centralizing rules and validation making the intent explicit and preventing bad data from spreading. You don't need any specific architecture or special frameworks to get these benefits. Though you can use pideantic if you want to, but you don't really need them. You can build them with simple Python classes and data classes to get all the benefits. But I'd love to hear your thoughts. Do you use value objects like this in your code? Do you think it makes sense at all? Let me know in the comments. Now, if you want to explore more software design patterns like this one, check out my design patterns playlist right here. Thanks for watching and see you next

---
*Источник: https://ekstraktznaniy.ru/video/20752*