Best video to date. Loved how it clearly showed step by step how a code base degregates with "just one more change request" and how to propertly fix it. I hate these flag bools with a passion.
@rethardotv58743 ай бұрын
That’s the reason why you make changes to the architecture if needed. If you just append features you get an unmaintainable mess within less than a year.
@pl4gueis3 ай бұрын
@@rethardotv5874 I'd rather have a solid foundation and make it right the first time. There is no need for bools. Imagine if architects made buildings how we make software: "Just start building. We can always change the architecture later" Sure some flexibility in software development is required but most just go all in on that and don't plan anything at all anymore.
@rethardotv58743 ай бұрын
@@pl4gueis that’s not what I meant. When requirements change the architecture need to change to avoid such nonsense as adding bools.
@pl4gueis3 ай бұрын
@@rethardotv5874 Ah sorry I misunderstood. Yeah I agree I wouldn't call it 'architecture' in that case. I'd call it Domain Models should change and because we remember primitive Obsession we shouldn't use simple non expressive types like bool.
@cj82-h1y3 ай бұрын
The boolean is not my friend? Well, in all fairness, it's "a bit on again, off again"
@zoran-horvat3 ай бұрын
@@cj82-h1y And when you stumble upon the rock, it's the gravity that made you fall. In all fairness.
@KazisCollection2 ай бұрын
Amazing pun
@TPInspector3 ай бұрын
Basically, if you have a class with an attribute that represents multiple states, you can use subclasses to represent each of these states. This approach makes the code cleaner and more organized. It also makes it easier to handle changes, which is important because clients often have additional requirements or changes in the future. Thanks Zoran
@zoran-horvat3 ай бұрын
@@TPInspector Only as long as there is no second level of inheritance. That is why I pay so much attention to make each class a composition of single-level hierarchies. Soon enough, there will be discriminated unions in C#, implemented as a single-level inheritance. They will just click with any design based on these principles.
@TPInspector3 ай бұрын
@@zoran-horvat Oh yeah. I did not even think about that. Thank you :)
@fswerneck3 ай бұрын
@@zoran-horvat So cool! Discriminated unions is something every language should have!
@RandomGeometryDashStuff3 ай бұрын
hard not to make root class represent invalid state
@OatmealTheCrazy3 ай бұрын
@@fswerneck Personally, I'm against apartheid
@nickbarton31913 ай бұрын
How I giggled at the first law of customer requirements, that they never stop. The second law is like unto it, they want it yesterday. Great job Zoran.
@slipperynickels2 ай бұрын
third law is turns out they didn’t need it and it was just a whim from a non-technical manager.
@holger_pАй бұрын
It's more like, they are unable to say what they want.
@nickbarton3191Ай бұрын
@@holger_p Or they explain it in terms of an implementation without describing the problem they need solved.
@neeeeeck90053 ай бұрын
This is what differentiates good programmers, if programmer doesn't understand or doesn't want to understand a hollistic picture of the product and requirements they're developing, they would not come up with this solution. This solution shows understanding of not just programming, but analysis and thorough understanding off business logic. Always understand what you are coding, and why you are coding it, before you start coding it.
@Tekner4363 ай бұрын
going from a team that designed the database first and wrote code around it, starting to use ddd is a huge breath of fresh air
@collynchristopherbrenner32453 ай бұрын
@@Tekner436 This story is all too common. The DB becomes the culture instead of code being the culture. Code is more flexible than RDBs by design - it's a matter making sure you use the code effectively and each tool for what it was meant to do.
@OtakuNoShitpost2 ай бұрын
I don't know that "Represent that with another class which wraps couple of trivial properties" is a matter of not understanding the requirements or not listening to the customer...
@000dr0g3 ай бұрын
Marvellous video. As an F# user, I've been drifting away from using explicit class inheritance, but it's very inspiring to see it well done. Starting with a bad design is a great plot device.
@fswerneck3 ай бұрын
Definitely the right way of writing code. I was butting heads these days exactly because of this at work. Coworkers who are closer to management got to include code with potentially invalid states in it, and a boolean. Like: thing.is_partnership: bool, thing.partnership_description: str | null, and thing.partnership_enddate: datetime | null. During code review I pointed how the boolean is unnecessary, and that it would be better for those fields to be tightly coupled into a new type, Partnership, that could itself be nullable in this context, as in, thing.partnership: Partnership | null. If it's null, there's no partnership. If it isn't, then for certain there will be a description and an end date. They ignored my comments and merged anyway.
@zoran-horvat3 ай бұрын
@@fswerneck Sounds similar to my demo example.
@fswerneck3 ай бұрын
@@zoran-horvat Shockingly similar! That tells us how common this is.
@zoran-horvat3 ай бұрын
@@fswerneck Oh, there is no mystery there! I made this video because I saw that a million times. And they all look alike.
@NullzeRT3 ай бұрын
First thing I thought of is a Rust's enum, which is algebraic type, meaning that each of it's variants can contain it's own data. And it turned out was the correct answer with analogous construction in C#. In Rust this solution comes to mind sooner, since its a very common practice and one of the core features of the language.
@zoran-horvat3 ай бұрын
Rust is doing that very efficiently, both in terms of space-time performance, and in terms of coding effort.
@CraigLuna3 ай бұрын
The way Zoran implemented this is how I simulate Rust ADT in C#. Perhaps the intro of unions will also allow use to tighten up memory as well
@StefanH3 ай бұрын
An even slightly flawed model can have massive implications and introduce pain points. Great showcase of how data models should be designed to make invalid statd impossible to represent
@batek343 ай бұрын
I really can't follow a single video, it starts in the middle of an already started project, the narration is abstract and poem-like, the explanation is confusing and all over the place. He seems like a good developer, not a good teacher, the whole point of the video was if you have a class with an attribute that represents multiple states, you can use subclasses to represent each of these states, and i got that from a comment, HE never said that, he was all like "In the realm where code does weave, A tale of logic, pure and brief, There lies a class, a single form, With attributes that should transform". Personally I think Milan Jovanovic is the best .net online teacher at the moment, him and Mohamad Lawand.
@Ownage4lif312 ай бұрын
Yea same I was confused because of the title. This seems to be mostly for people who already know what he's on about. Just don't know why his title is clickbaity.
@Gremirz3 ай бұрын
This OOP approach is just beautiful. You good sir just sold me your course.
@yimyim1173 ай бұрын
Good demonstration of using inheritance to abstract away binary states. Although a data driven design approach would also yield high simplification. Why store the information of being published at all? You can manage these states as containers, e.g. a list of published books, a list of unpublished ones etc. But of course it depends on the context.
@Internetzspacezshipz3 ай бұрын
Every problem and solution always comes down to context. But you can always choose the context for the next problem by solving the current problem in a predictive manner…
@chlojolo3 ай бұрын
I am grateful that the next time I have to explain to someone that bool is not their friend, I will now have the option to send this video. With any luck, they may even heed it.
@mkwpaul3 ай бұрын
Great video, superbly explained and illustrated. I just wanna point out that this isn't just good practice when doing ddd or OO but any programming style be it functional, purely imperative, data-oriented, or in this case DDD/OO. Properly modeling your data, and making invalid states unrepresentable on the type level, is the biggest lesson to learn here. And this modeling generally requires much more fine-grained types than many developers are used to. The "IsPublishedBefore" check is implemented here as a virtual method on the base type, but could just as easily be a static extension method with a swtich expression instead in a functional or imperative data-oriented style. Of course there are always exceptions, where booleans are truly domain >data< and not a misguided way of implementing a more complex piece of information.
@zoran-horvat3 ай бұрын
@@mkwpaul I agree with your analysis.
@markky2123 ай бұрын
Please don't stop your mission Ser!
@zoran-horvat3 ай бұрын
I'm only warming up!
@yeti259343 ай бұрын
I saw many issues with the code here, few of which were really caused by the boolean. Also, that "a boolean never walks alone" line was cute, but completely untrue. Please refrain from spreading catchy lies.
@aflous3 ай бұрын
You missed the point here. Booleans as a primitive type are ok to use and abuse when modeling a simple property or attribute. The phrase he used applies when using a boolean in domain-driven design, which stems from how you choose to express a certain business-related state.
@roll-outcommanders65203 ай бұрын
I find your videos tantalizing, provocative and very informative. Although I have come to the end of my software engineering career ("career" use as verb here) I am sure I will still take interest in what you have to say although I will never get to engineer any of your insights. Thank you Zoran and keep up the good work.
@juliogomez37903 ай бұрын
You are a true genius of clean design! I've learned a lot with this video and I am excited to continue learning stuff with your videos. Thanks for sharing your knowledge with the world!
@josebarria32333 ай бұрын
Good video. Note that this applies not only to domain models.but also to game design.. instead of representing states with a bool, just take the time to make it more complete
@tryoxiss3 ай бұрын
I know this is an example in Java (OH, its C#, they are so simillar I missed it), so this solution wont work here, but if you are using a language with sum types (Rust, functional-programming), you can use an enum: enum { Published(Date), Scheduled(Date), Unpublished }
@zoran-horvat3 ай бұрын
You are correct. C# currently uses record types to the same end, so your proposal is essentially the same design but a slightly different syntax.
@britbuttmcbooty92213 ай бұрын
C# has enums I just used them a couple of weeks ago for a final
@harrytsang15013 ай бұрын
In C++ we have union class, but the way rust or scala handles it with match case is so much nicer
@Vennotius3 ай бұрын
I will now keep an eye out to discover this boolean law in my designs. If I see it for myself, it will click. For now I take note.
@5cover3 ай бұрын
So satisfying to see that code shrink and simplify!
@zoran-horvat3 ай бұрын
@@5cover Many programmers think that simple state makes simple code. They don't see how complex the methods must be to operate on that.
@baranacikgoz3 ай бұрын
That's why I think one must master the strategy pattern before write code. Excellent video.
@MaxPicAxe3 ай бұрын
Yes, this is where I like to use enums with associated data. In this case we wouldn't necessarily wrap the whole book in the various possibilities, but rather just the section of data that depends on it, so in this case just the Date would get wrapped in the various possibilities.
@MaxPicAxe3 ай бұрын
Edit: seems like that's what you did in the end
@zoran-horvat3 ай бұрын
Yes. C# uses record types for that purpose, and they are really efficient in every respect, including coding time.
@s0psAA3 ай бұрын
How would you store this Publication record in the databse, regarless of what actual instance it holds, serialize it to a json and store it in a single column?
@zoran-horvat3 ай бұрын
When there is a 1-1 relationship to the containing entity, then , you can store any of the possible values in that entity's table using strategy like table per hierarchy. That might create several strongly-typed fields in the table. There is no need for JSON, though it is a possibility as well, if you wish it that way.
@nanny072 ай бұрын
@@zoran-horvat IMHO, this should be a dedicated video because it's something that it's hard to imagine. It will be interesting to see the difference between a RDBMS database and a NoSQL one
@conradrobinson79413 ай бұрын
Hi mr Zoran great video but mainly I really really like your voice, tone, and pace of speaking. I will be watching all the others now.
@gonzo1913 ай бұрын
I see what you did there. A response to the confusion from the junior programmer video's book example.
@zoran-horvat3 ай бұрын
Yes, this is the follow-up video. I plan a few more, to clear other confusions that happened there. P.S. It was the seniors who were confused. Funny enough, in quite a few comments there, people who declared as juniors were explaining their mistake, but seniors wouldn't listen.
@unexpectedkAs3 ай бұрын
Really good video, really good examples and step by step presentation. Will be sharing it a lot!
@umdi33373 ай бұрын
One of the most brilliant videos I have watched recently. Next one should be about how to persist all these in a relational database 🙂
@zoran-horvat3 ай бұрын
@@umdi3337 Actually, persistence is still not an issue at this level of complexity.
@MarcoAntonioRosadaSilva-h9c14 күн бұрын
I don't know how to explain this, but watching you code feels like watching someone paint.
@zoran-horvat14 күн бұрын
@@MarcoAntonioRosadaSilva-h9c I try to make coding enjoyable.
@marcotroster82473 ай бұрын
Well done. But not everybody has the luxury to refactor the domain model like that. Most of us work at legacy systems and try to bring the codebase into a state like before your refactoring and would be happy with that little boolean flaw because no one really pays for that refactoring you showed. It's kind of unfortunate.
@DxCKnew3 ай бұрын
In the end, you still need to map all these objects into a database. While possible with some ORMs, it is somewhat a challange and complicate things by iteslf because databases are naturally not designed with OOP in mind, imagine a big solution with tons of these, so I generally don't like to mix inheritance and database objects.
@zoran-horvat3 ай бұрын
@@DxCKnew Persisting this object model with an ORM is straightforward and it is managed entirely in the ORM configuration. Persisting it with document storage is more-or-less a single line of code. On the other hand, implementing domain rules, object interactions, business validations - that will quickly grow to hundreds of types and thousands of lines of code. I would really appreciate it if the programmers one day stopped asking "how do I save this" all day round and started asking "how do I implement 470 business requirements the customer has filed since the last year".
@dusanknezevic90723 ай бұрын
Using boolean as a flag shows a problem with modeling. Modelling concepts with simple boolean flags leads to delegating responsibility to caller and worse yet, to cartesian explosion of possible states. Some of these are invalid and shouldn't even possibly be represented in code and at runtime. It's just sensible to use type system to capture concepts and states whether its OOP language or FP or logic paradigm etc.
@holger_pАй бұрын
I don't see any difference, to use an enum, as a flag. a boolean actually is also an enum.
@Art1x_y3 ай бұрын
If it comes to storage in the database, then everything can be the other way around. Then filtering (search) by Boolean variable will be much faster. And even just non-nullable type filtering is also always faster than nullable, so the decision always depends on the context of its use. There is no ideal solution, there are solutions suitable for specific tasks :)
@zoran-horvat3 ай бұрын
@@Art1x_y You have a point there, but I must add to it. Even in storage, I would keep the timestamp if the event rather than a flag whether the event happened or not. That would help support diverse queries at virtually no additional cost.
@7th_CAV_Trooper3 ай бұрын
The domain model and the data model have little to do with each other. Of course you're going to transform the data before laying it to rest.
@NGC-rr6vo3 ай бұрын
damn, thats eye-opening
@perplexedon98343 ай бұрын
This is where enums and options are so powerful. Rust isnt the only language, but its the most famous for it. let released= Option means that released could hold either "None" or a ReleaseDate. This forces every function that interacts with release to consider whether it could have failed to be release. None is not null, it is a true value thay can be handled. This sounds like a chore, but usually just means a single extra "?" in the function to early return and branch the logic if its None. Always cleaner than exception handling. If you want multiple possible states, its as easy as defining an enum that could be one of a set of states, some of which can hold data: enum ReleaseDate { Unreleased, Range(Date, Date), Specific(Date) }
@zoran-horvat3 ай бұрын
@@perplexedon9834 Yes, enums with values in Rust and Java is what C# currently implements as records derived from an empty abstract record. It looks like C# will soon attain discriminated unions that will simplify the syntax a bit.
@yuryschkatula90263 ай бұрын
It's not about the bool itself, the story is about introducing present-time properties into models that intend a period of time. The same thing about "Age" property, it is not a bool one for sure. Can you spot the domain issue nevertheless? Does it mean numeric types are not your friends?
@zoran-horvat3 ай бұрын
Primitive types are not your friends, to be precise. You must contain it in a meaningful type that is responsible for them, like an older cousin. A devastating side-effect if not doing so is insatiable growth in the number of fields per class. This Book in my demo would have a 15-argument constructor already, and I have only just started developing the application!
@2Fast4Youtube3 ай бұрын
So is pretty much: If you know you will have more than 2 states, don't use a bool to represent it
@zoran-horvat3 ай бұрын
@@2Fast4KZbin It's more than just that. Add: If you know there will be behavior on that bool, make it a standalone type. And a few more ifs like that.
@fallegapyro3 ай бұрын
Everyday, something new is "not my friend"! Hell nah! Strings, booleans, singletons and others are my friends!
@zoran-horvat3 ай бұрын
@@fallegapyro How do you contain code duplication and code inflation, then? Looks like you are stuck with the data model, but somehow ignore behavior.
@NikorouKitsunerou3 ай бұрын
This might be a point against "code first" databases. Some fields deserve their own tables.
@zoran-horvat3 ай бұрын
I don't think it is big enough to consider it relevant.
@Rick1045473 ай бұрын
What is your preferred approach when introducing persistence (with EF core for instance) with a rich domain model? I find that a rich domain model and persistence don't always like each other. There are some ways around it with custom converters etc but they all seem to have downsides by not fully supporting all operations for instance. Also the shape of a domain model might be completely different from how its persisted in a database. Iam leaning towards using a separate persistence model with types that are friendly to persist (which usually are primitives). How do you deal with this? Do you use different approaches depending on the situation?
@zoran-horvat3 ай бұрын
@@Rick104547 I do all the EF Core setup in the infrastructure layer, i.e. not in the domain, say via attributes. in that way the persistence mappings do not pollute the rest of the code base. EF Core is progressively becoming quite powerful, with the resulting relational schema approaching the best one would do in an entirely manual design. There is one notable detail regarding EF that particularly annoys me: Many-to-many relationships. I insist on one-to-many in models, but EF insists on having its many-to-many part of the domain model. Then I must hide it in private fields and only expose that one-to-many relationship in the model as I wanted. The example in this demo is books and authors, which naturally form Many-to-many relationships in the database, but the book only observes many authors of its own, i.e. one-to-many.
@marko57343 ай бұрын
You can use ComplexType attribute for value objects
@adambickford87203 ай бұрын
That's a big downside to this approach; you'll end up writing many 'mappers'.
@Rick1045473 ай бұрын
@@adambickford8720 that's true which is why I don't always do this. But then the 'domain' model ends up being a compromise as it also needs to work well with persistence. It's getting better with recent updates though.
@zoran-horvat3 ай бұрын
@@adambickford8720 Well, no. Why would not using an ORM be a big downside? Will that not make functional design less favorable, where in fact there is plenty of evidence to the contrary?
@gregorymorse84233 ай бұрын
There are more cases where Boolean flags are correct for state than these narrow areas of bad design with redundant ambiguous uses. A bit over dramatic and over selling. Try giving this talk at a hardware company or a BIOS or kernel driver provider. Their eyes will roll so far back in their heads it might cause permanent damage. So just noting this is true on very high level data or abstraction models. In low level code, on the other hand, this it is generally optimal to use certain contextual Boolean flags.
@zoran-horvat3 ай бұрын
Why would I talk the same to a BIOS company as to business application developers? Do I look like an idiot?
@gregorymorse84233 ай бұрын
@zoran-horvat lol I only meant the title was a little bit click bait without context. Granted I suppose it is considered fair game to do that. I assume that was a rhetorical question :)
@zoran-horvat3 ай бұрын
@@gregorymorse8423 It cannot be a clickbait if the title only speaks of Boolean in a negative sense and the video only covers Boolean in a negative sense. I don't do clickbaits.
@user-tk2jy8xr8b3 ай бұрын
Sometimes bools go alone. For example, a client setting controlling whether an IRR result should be annualized or not. There is no functional dependency with any other part of the application state, it only affects the output number. Feature toggles also go alone. Those are hardly domain models though. Otherwise, yes, bools usually have some sort of a functional dependency from another value, like in Nullable implementation. Not only bools, any type, potentially. Maybe such dependency eradication should be called "model normalization", just as it is in DBs?
@zoran-horvat3 ай бұрын
@@user-tk2jy8xr8b I've done so many of those and I'll tell you're missing the world of opportunities if you only took a bool as the model. Consider a feature toggle that is the timestamp *when* the feature is toggled. Or a record of finalizing a transaction, which incorporates a timestamp, security information, external system reference, and a few other fields. A bool wouldn't walk alone in those applications either. The whole range of everyday requirements will require a redesign, you see. Some people fall victim to believing I am just guessing. They know nothing of a quarter of a century I spent doing business applications. I *do* know how incapable boolean models are.
@user-tk2jy8xr8b3 ай бұрын
@@zoran-horvat what if I model dynamic computations over booleans (which I do) so that boolean values directly represent the domain? Sometimes a bool is just a bool.
@zoran-horvat3 ай бұрын
@@user-tk2jy8xr8b That is what I said in the video the other way around: a bool is a good answer to a question.
@yurisich3 ай бұрын
Bools export work that's meant for compilers onto people. This is why feature flags work, it's a rare example of the friction they introduce serving an intended purpose.
@zoran-horvat3 ай бұрын
Think about a feature flag that has a time when it is toggled. Then a feature flag that has an interval, such as the one we use in A/B testing. I've never implemented feature toggles as a Boolean flag, but always as a method that returns boo and encapsulates logic. It feels much better.
@v0id_d3m0n2 ай бұрын
Wow - it's beautiful!
@ThugLifeModafocah3 ай бұрын
I did not get one thing. Why were you using the new Published as param to Release if one another possibility would be that the publishing was just Planned? Wouldn't requires that the selection of what implementation of PublicationInfo to be dynamic?
@Foxsterdota3 ай бұрын
Great stuff! Out of curiousity, is there a particular reason why you made PublicationInfo an abstract record (especially one that does not contain any actual implementation like the one in this example) rather than an interface (like IEdition here) that the concrete classes could then implement? Perhaps it's mostly, if not entirely, a question of 'actually being' vs. 'being able to', if that makes sense...? As in the PublicationInfo is/is not published vs. an IEdition being able (an action) to be advanced to a next edition.
@zoran-horvat3 ай бұрын
@@Foxsterdota It's only my feeling that it makes them look more like a discriminated union, that is all. If the current proposal for discriminated unions passes, they will look like a class out of the box.
@mohammadtoficmohammad35943 ай бұрын
Thank you good idea please keep going but please we hope you can present your ideas in better way, if people follow this way or another that does not mean they are doing mistake or they are juniors , but there are many aspects must taken into account
@fennecbesixdouze17943 ай бұрын
You could have fixed the bug by simply introducing runtime validation into the constructor to ensure that if isPublished is passed as true, then a publication date must be provided. You could have a single concrete IsPublishedBefore method on the Release, which simply would return false if isPublished is false, and do the date logic otherwise. Your Program.cs code would look exactly the same except it would call isPublishedBefore on the Release, and the runtime type inspection would be replaced by checking the boolean isPublished. If you're worried that the consumer will sidestep your IsPublishedBefore method and still inspect the isPublished flag and hand-roll their own query depending on it, simply make isPublished private but still call for the boolean in the constructor with the same runtime validation. The only difference is that the constraint would be imposed at runtime rather than in the type system. Which is a fine trade-off to avoid writing all the extra code to introduce an additional abstract class with all of these overrides
@zoran-horvat3 ай бұрын
@@fennecbesixdouze1794 The suggested method fails at runtime. I don't like having code that fails. I prefer code that works with no failure.
@twenty-fifth4203 ай бұрын
I dont use Booleans, I just use 0 and 1 and I make sure the compiler doesn’t forget it. 🔫🔫
@AK-vx4dy2 ай бұрын
Great video about object modeling but.... Do you have video with concise and sane method of mapping such type to database for example two database fields in one table ?
@zoran-horvat2 ай бұрын
@@AK-vx4dy There are a couple of videos on other topics where this model was indeed persisted, using EF Core. I am currently working on another demo which might end up demonstrating EF Core persistence if this model step by step. As of now, it is important to know that EF Core supports all the elements shown in this video.
@AK-vx4dy2 ай бұрын
@@zoran-horvat Yes i saw but I'm interested especially in "flattening" such simple subobject to multiple files possibly in the same table (I know you may have other ideas but many times I work with existing already databases which I can't change or like in this case it seems artifical to split to many tables) without too much boilerplate.
@NGC-rr6vo3 ай бұрын
hi, what type of content do you have on patreon? may be there is table of content somewhere?
@zoran-horvat3 ай бұрын
@@NGC-rr6vo Basic membership gives you access to the source code from the videos. Advanced membership adds access to the new video course on object-oriented programming and design I am working on right now.
@Merssedes2 ай бұрын
After watching this video, I've one question: why so many classes?..
@zoran-horvat2 ай бұрын
@@Merssedes Because there is a dozen methods operating on each. If you put it all into 2-3 classes only, each would be longer than 1,000 lines single-handedly and contain tons of mutually unrelated code. Think for yourself. Could you define a production-grade bookstore model with less than 10,000 lines of code?
@Merssedes2 ай бұрын
@@zoran-horvat What model are we talking about? Because my knowledge of C# does not contain such term.
@zoran-horvat2 ай бұрын
@@Merssedes Domain model. It has nothing to do with C# in particular. Every domain application has it. It consists of domain classes and other types and methods/functions defined on them.
@Merssedes2 ай бұрын
@@zoran-horvat So all it means is that I just missing context for the video. Because i've never heard term "domain model" before.
@Karloffspring2 ай бұрын
My biggest take-home was: use your language's type system to make impossible states impossible to represent. I loved the observation that a boolean variable never walks alone. (I'm not a C# programmer, but this vid was useful to me anyway.)
@DavidSmith-ef4eh3 ай бұрын
Let me play devil's advocate again. I can see how that makes your code cleaner. But you still have to persist it in the database and some frontend api needs to call it using rest/graphql. A boolean toggle is so much simpler for them, or at least an enum...
@DavidSmith-ef4eh3 ай бұрын
I mean, you could still have objects nested within objects. But you'd persist it as 1-to-1 child relation in db? I can see that working..
@pl4gueis3 ай бұрын
I'd argue that for the consumer a boolean toggle is so much simpler..yeah to introduce bugs. What is the correct state combination for a book that is scheduled to release next year and the 'IsPublished' flag? Will the bool be true or false? I mean right now the book is not published but next year it will be published.
@DavidSmith-ef4eh3 ай бұрын
@@pl4gueis in that case I'd use an enum + release date. Or just the release date, without any booleans. The good thing about a boolean, it can be a 1 byte db column. But there is no perfect solution, obviously.
@pl4gueis3 ай бұрын
@@DavidSmith-ef4eh What stops you from saving it as a bool in the database? The way you model your domain is how the business works. It does not have to reflect that 1:1 in the persistance. You can just map it later.
@zoran-horvat3 ай бұрын
@@DavidSmith-ef4eh Keep in mind that the model also contains other polymorphic components, such as edition. The release date will quickly become more than just a date (the day is often unknown, only month and year are known or recorded; on dome old books it's only the year that is printed on it). Soon enough, the book object's state would grow to 20 fields or so, and constructors/factory methods would sustain combinatorial explosion, to the point where it becomes impossible to manage them. Going down that path would lead to having dozens of factory methods just to construct each combination of states. Polymorphic components solve all these problems at the outset. The trick is in keeping all mini-hierarchies only one level deep.
@NikolozLatsabidze-t6h3 ай бұрын
Why you did not create a method inside Publication interface which is returning this 2 integers, logic is coupled to the Publication class, since publication state defines which integers should be returned in certain cases, in addition you can also remove this if else blocks and type checking for published instance
@MrCumberlander13 ай бұрын
Rust enums would be perfect for this purpose.
@zoran-horvat3 ай бұрын
@@MrCumberlander1 Rust enums are identical to C# records I used in the video. Same thing, different language.
@RoboDragonJediKnight2 ай бұрын
Alternatively, could be solved with some finite state machine patterns. A good section in "The Pragmatic Programmer" on this pattern.
@zoran-horvat2 ай бұрын
@@RoboDragonJediKnight The goal of the finite state machine is in mappings that are closed to the machine. Here we have the problem of mapping the states of the supposed machine into the values that are not its states. The range of the functions we need is not the set of machine states.
@trustytrojan3 ай бұрын
why add a boolean when you can simply check whether the PublicationDate has passed?
@zoran-horvat3 ай бұрын
Because it can pass without the book actually being published. Life is sometimes like that.
@sorakatadzuma80443 ай бұрын
So, I agree with your ideas and the changes that you propose to make code cleaner. However, I am curious how this would work in a language that doesn't support polymorphism or your project constraints don't allow for polymorphism? I'm considering things where Data Oriented Design/Programming is preferred or required, and languages like C or Rust is used.
@zoran-horvat3 ай бұрын
Rust supports this solution via enumes with values, and so does Java. In C# it is records right now, but as it seems they will soon evolve into proper discriminated unions. It is actually the discriminated union I am using in this demo, not inheritance in the traditional sense.
@HkanAktas3 ай бұрын
I don’t understand why you wouldn’t get rid of the Boolean completely. Tell me if I’m missing something but the Boolean is not bringing any new information the date didn’t implicitly contain. Future date => planned book Past date => published book No date => unplanned book I also understand that you’re bringing context to an otherwise poorly defined flag, but the example is teaching another bad practice, namely unnecessary data denormalization.
@zoran-horvat3 ай бұрын
@@HkanAktas What is the state of the book that was planned for a certain date after that date passes but the book didn't get out?
@Mystic9983 ай бұрын
@@zoran-horvat It's an invalid state. But then so are lots of states that are technically allowed but not accounted for in the code (publication date = 20134-01-01, title = 'esfjesfjesdjf', etc.) The extra state information gives you traceability into what's probably a common occurrence (book is delayed), but that extra state has to be updated when the book is published either on time or later, so your data will somewhat regularly be in an incorrect state. Using just the publication date as an indicator means that as long as books publish on time most of the time, most of your data will be in a correct state without you doing anything. But then you have to reconcile your supposed publications with your actual publications and fix the dates. If I were doing it, I'd use the date to check if something is published for reporting purposes but also use the (not) Published state information to trace books that are potentially delayed/in need of correction. However, all or none of these are valid choices for dealing with the problem depending on the situation.
@HkanAktas3 ай бұрын
@@zoran-horvat if that's a possible case, the code is not able to represent all possible realities. All that code in the end of the video is telling me is that a book can either be published or not published. Today is Aug 16, and if I see a record in the DB for publish date of Aug 10 but the `IsPublished` flag is false, I have to *assume* that it must have been planned. The code is not telling me that. If this planned-but-not-published state is possible, I would still get rid of the boolean flag, and add an enum state instead. Pseudo code: ``` enum ReleaseState { NotPublished, Planned, Published, } ``` With the enum, I can now distill the information that if a book's release state is `Planned`, the release date is a planned date. And if the date is in the past but the state is still `Planned`, I can know that it was planned for then, but it didn't became the reality. The code no longer depends on an *implicit* plan-to-publish state, and the devs no longer need to put extra cognitive energy to deduct that.
@zoran-horvat3 ай бұрын
@@fusedqyou So, it is date and date vs date and bool, with all other issues remaining? I thought you would resolve the issues in code, not trade one scalar type for another.
@HkanAktas3 ай бұрын
@@zoran-horvat Ugh, I wrote a detailed answer the same day, but either my internet failed me or KZbin decided it's not worth to be displayed to the world. In summary, if the state you mentioned is possible, wouldn't it be better to use a state enum instead of a boolean? Boolean flag is technically a state enum with two implicit states, but IMO it's clear that the product has a third state in reality. The "planned but didn't go out" state. I would rather represent that with: PublishState.Planned & a past date, instead of a confusing IsPublished = false & a past date.
@AndersBaumann3 ай бұрын
This refactoring looks jolly good until it is time to persist the Release class and PublicationInfo record with EF core or NHibernate.
@zoran-horvat3 ай бұрын
@@AndersBaumann Actually, it is quite straightforward with EF Core.
@AndersBaumann3 ай бұрын
@@zoran-horvat May we see that?
@zoran-horvat3 ай бұрын
@@AndersBaumann There are a couple older videos with full persistence of this model, and there will be a few more to come.
@AndersBaumann3 ай бұрын
@@zoran-horvat What are the names of the older videos so I can find them?
@zsoltesse9873 ай бұрын
@zoran-horvat this is such a valuable lesson! It really is. Also an interesting one. But watching your video makes me feel bad. Not (only) because I'm ashamed that I'm not doing things right yet, but because I'm struggling to follow you, even though most of the time you're not speaking fast at all. I watch your videos at 0.9x speed, but I still have to watch some parts several times. I think there are several "problems": 1) a) The way (animation?) you add new code is usually too fast. Also, you're talking about something else at the same time (I mean, you're not reading the code you're showing). 1) b) Your code is not in the usual style. Which is fine, because you often show it to help us learn good coding styles. Just be aware that many of the viewers will find it a bit hard to read, they will need time. 2) Your rhythm is a bit too fluctuating. I know it can be a rhetorical tool, but sometimes it makes it "challenging" to follow you because it's so unpredictable. 70% of the time I could watch your videos at 1.25 or 1.5 if it weren't for the visual part, but 30% of the time is almost too fast (especially for non-native English speakers). Please consider changing your presentation style a bit.
@zoran-horvat3 ай бұрын
@@zsoltesse987 I'm changing my style all the time. The points you make are valuable, thank you.
@shadeblackwolf15083 ай бұрын
I think booleans can work in the model, but in this case, is_released is a derivative of the release date, is it not? we can add an isReleased method by checking if the releasse date is a forecast or a date in the past, which can be implemented by comparing it to now. And when you bring in the nullable, it's starting to smell like Release needs to become polymorphic. To talk an example from my own experience, as part of a customer object we model a list of services for that customer. The services have a 3-value semantic. They can be present and active, present and inactive, or not present. Present and active means the customer can use the service. Present and inactive means that the customer is eligable for the service, but they do not have it, and absent means the customer is ineligable for the service. Currently this is modeled as a list of Service objects, that each contain a boolean state indicating if that service is enabled, and it's been servicing us well. The main upside this has, is that the services themselves are data, and as such can be added and modified semantically without development impact
@zoran-horvat3 ай бұрын
@@shadeblackwolf1508 On most occasions, a bool is a good answer to a question, but an incomplete representation of the state.
@ferinzz3 ай бұрын
I think that for his example to apply to your system the bool would be representing some other value. For example if active was tied to a contract with an end date. In that case you could/ should derive the bool from the presence of that end date as well as whether it has been passed in time or not. instead of declaring a bool and also having a contact end date.
@ParkourGrip3 ай бұрын
Ok. Now I'm curious on how you would store this model with a tagged union in a relational database. Would the relational model have a bunch of nullable attributes with custom constraints that ensure that only the fields of 1 and only 1 tagged union variant is not null? Or would you have multiple tables, one for each tagged union variant, with 1:1 relationships to the "main" table?
@zoran-horvat3 ай бұрын
@@ParkourGrip You can store them in the table with the containing entity and a discriminator. It works very well. On the other hand, "a bunch of nullable attributes" already speaks about the feature's complexity we are dealing with here. It's not bool. It is several possibilities (more than two for sure), each represented with a different set of attributes.
@rotamrofsnart3 ай бұрын
Is there any literature or article about this? I found it a bit hard to grasp and I would like to learn it at my own pace.
@zoran-horvat3 ай бұрын
There are many books that would help you: Martin Fowler, Eric Evans, Bertrand Meyer, etc.
@SteinGauslaaStrindhaug2 ай бұрын
As a fronted programmer I tend to always assume every field can be null or wrong type because the data comes from a server I don't control or a browser API I cannot control or worse from an incompetent user. So I always write code to handle nonsensical combinations of data. Assuming the data to be correct because you designed your data model to only allow sensible configurations is a luxury you only have if you're not having a database, a network connection, or user inputs outside your control. Also unless a boolean is the completely wrong type to use; I'd say that any combination of bools and nulls are possible and to be handled even if they are unlikely. E.g. a book can be published but lacking a (known) publication date. So having isPublished == true and publication date==null is something you should handle.
@zoran-horvat2 ай бұрын
You are speaking of data sanitization, which belongs to the system's surface, not interior. Once the data are in, they must have passed validation already, so there is no need to repeat any checks again. In strongly-typed languages that check is already performed on the assignment for you, so you design sanitization via the design of types. In persistence, schema validation performs sanitization for you before materializing the object graph, leaving nothing to you to repeat. Even in weakly typed languages, separating sanitization from domain code is a generally preferred design. Skipping that step would inflate your code by a factor of 10 with no benefits whatsoever.
@SteinGauslaaStrindhaug2 ай бұрын
By bool being the wrong type I mean when you realise you need multiple bools that are almost always evaluate together, like isShipped isReceived isCompleted etc. Where most combinations are invalid you probably want an enum instead. Only use bool if there only 2 states and they are always possible to be set in either state independent of other fields. IsArchived is fine as a Boolean since you can decide to archive something at any point in the process. But anything modelling a sequence of steps is probably not a set of booleans.
@zoran-horvat2 ай бұрын
@@SteinGauslaaStrindhaug Where do you implement a domain operation when the state is represented by a primitive type?
@impero1013 ай бұрын
Could you make a part 2 where you demonstrate persisting a model such as this one? Both using an ORM with a relational database, and with a document database.
@zoran-horvat3 ай бұрын
@@impero101 I will be making a few other videos that demonstrate that as well. But note that there are probably a dozen of videos already on my channel that demonstrate other aspects of the bookstore application, which use EF Core in persistence.
@timseguine23 ай бұрын
I like your fixed model for the most part and I agree with the main point of the video. BUT, I tend to be very "vibes" driven when it comes to programming so I often "know" something is "wrong" before I can explain why. And my experience has shown me it is a good idea to follow that feeling. So I actually had to think for a while why I felt your final model was (for me) ultimately unsatisfying. First the feeling: adding "isA" checks outside of the context of a statically checked variant type always feels gross to me. I know C# doesn't really have those yet, and that seems to be your intended model here even though the syntax doesn't really allow that. But even so, I object to it on semantic grounds. My objection is subjective and minor (in this case I am not sure it is relevant), but the issue comes if for some reason we get a requirement that needs a new type of PublicationInfo. Even then it could only bite you if that new type was also considered to have IsPublished == true. But if we follow the principle that things which have changed in the past are more likely to change in the future, then it seems reasonable to want to future proof this kind of extension. I'd tend to prefer from a type theory perspective that EITHER the set of concrete classes is limited explicitly at compile time OR that the classes are built with the possibility of another type being introduced in mind (the type hierarchy usually isn't closed even if all the concrete classes are sealed). And in this case that requires IMO that the behavior of PublicationInfo is fully specified by its available methods. Concretely that brings me to what I would have written when you said what the new requirement was: First off I agree, the requirement seems to be screaming for a new type that encapsulates the two connected pieces of information. But in my opinion, the bool isn't so far off from what we really want. From a business logic perspective we want an IsPublished query. And it is the responsibility of the PublicationInfo type to provide that query (in this case without a backing field). so the other types would have: public bool IsPublished => false; and Published would have: public bool IsPublished => true; I could see an argument against it in this case that it is overengineered. But I get the impression that we are discussing a general principle and not this specific application. In any case I recognize there is a tradeoff. And the reason for me is that my suggestion seems less likely to break under maintenance.
@palapapa02013 ай бұрын
5:13 Why would need to add another bool to bring the total possible states to 8? I get that the point you are trying to say is that with a bool and a nullable, there are 4 possible states, but one of them is impossible but representable, which makes it bug-prone.
@Mothuzad3 ай бұрын
> Oh, some clever rage bait? > It's actually valid and clever.... > Got it. It's a "make invalid state unrepresentable" video. But instead of Rust or Gleam, we're doing it with C#. > Subscribes
@zoran-horvat3 ай бұрын
@@Mothuzad Did you see any rage in the comments? I see none. P.S. I may have not understood all too well.
@lukkkasz3233 ай бұрын
The Junior programming video was confusing to me, but this one made much more sense. I'm below junior level, my initial solution to this was to wrap both of these into into a Publication class, and also to replace the state with a method or even a simple property that just checks if the book is published, but I'm not sure if that was allowed here, because I don't know the exact requirements. Basically something like this: publication.IsPublished() => publicationDate < Date.Now(); Did I understand the video well (at least somewhat)?
@zoran-horvat3 ай бұрын
A touch of senior thinking: make the method receive the time when it evaluates. Otherwise, I agree with you. A bool is only an answer to a question, not the entire state.
@sf-petru2 ай бұрын
this is a nightmare to change when the logic is huge and the customer wants big changes
@zoran-horvat2 ай бұрын
@@sf-petru Do you have an example of a "big change" that would cause troubles? I see irrational fear behind your words.
@sf-petru2 ай бұрын
@zoran-horvat first thing, I love your videos... but: this is a very simplistic example. In real-world projects, as the code grows, you’ll need extra parameters, logic will evolve, the logic will change and you have lots of classes that override a functionality that is supposed to change. You’ll end up having to change a lot, often without knowing what might break
@zoran-horvat2 ай бұрын
@@sf-petru This is not a simplistic example - there are 20 types in it. Now, how would it help if I shrank it back to three classes? I don't want to repeat the video here. I believe I have already explained where that leads.
@sf-petru2 ай бұрын
@@zoran-horvat Apologies if I wasn’t clear earlier. What I meant is that the IsPublish methods are overly simplistic. With 15 years of experience in .NET, I’ve repeatedly encountered similar issues in large projects, especially those where specifications change frequently, and even the language versions and recommended patterns evolve. I really enjoy your videos and have learned a great deal from them. Your content is insightful, and I appreciate the value it brings, but I'm not always in the same boat.
@sf-petru2 ай бұрын
@@zoran-horvat Sorry, I wasn’t clear earlier. What I meant is that the IsPublish methods are overly simplistic. With 15 years of experience in .NET, I’ve repeatedly encountered similar issues in large projects, especially those where specifications change frequently, and even the language versions and recommended patterns evolve. I really enjoy your videos and have learned a great deal from them. I love your content, and I appreciate the value it brings, but I'm not always in the same boat
@colinmaharaj2 ай бұрын
I'm a C guy, there are bool and BOOL, I use both.
@johanavril16913 ай бұрын
Today I learn that c# doesnt have tagged unions
@zoran-horvat3 ай бұрын
But it has record types with a tag (sic!).
@templeofdelusion3 ай бұрын
The only problem I see here is that the bool is a field as opposed a method that checks if book was published... Surely you cannot be past the provided date AND have the book still be unpublished??
@zoran-horvat3 ай бұрын
@@templeofdelusion That is one of the issues with bool. The same issue does not exist when there is an object that specifies the effects of the event that happened. For example, you can ask for the status of the published book at a time prior to its publication date, and it would not be published!
@RandomGeometryDashStuff3 ай бұрын
10:16 Planned(date in the past)
@rahulmathew87133 ай бұрын
Why are u mixing business layer method and DTO properties in the same class Release. Isnt violation of SOC
@zoran-horvat3 ай бұрын
What do you mean by DTO properties?
@rahulmathew87133 ай бұрын
@@zoran-horvat the Release class we can see properties and methods
@alex382353 ай бұрын
This basically acomplishes the same thing as the typestate pattern right?
@zoran-horvat3 ай бұрын
@@alex38235 Yes, it looks very similar. That is the coding pattern known in all languages and coding styles, primarily for its safety and clarity.
@felixnotthecat4249Ай бұрын
how the record Published would be represented in the Database. How would I map this to EF?
@zoran-horvatАй бұрын
@@felixnotthecat4249 You can include them in an entity as a complex property. There are a few later videos where I used that technique.
@felixnotthecat424928 күн бұрын
@@zoran-horvat Thanks for answering.
@jakezepeda12673 ай бұрын
I hope this isn't a stupid question. What if instead you just omit the bool and use a nullable DateTime and compare that with current date to get the status? null = not planned > current = Planned
@zoran-horvat3 ай бұрын
@@jakezepeda1267 Actually, a planned date may eventually become past without the book being published. But the problem is in other aspects of using primitive types, and especially nullable types: the caller must implement domain logic, that logic will repeat endlessly across the code base, and the code base will be inflated by a factor of 2-3 because of this decision. For all these reasons, I consider the winning solution one that wraps those values and associated logic into a dedicated type.
@makemeafirewall3 ай бұрын
This is great, please explain how to use this with ef core, how should we use the polimorphic publishing state in the DB?
@zoran-horvat3 ай бұрын
Polymorphic state is not an issue, because EF Core supports that out of the box. A greater issue is when you develop an immutable model. That is where EF Core falls short. You must either add a few utilities to the DbContext to support immutable models, or use something else.
@CrapE_DM3 ай бұрын
Not how I assumed that would go, since I made an assumption that apparently you didn't want to make. I assumed that if the date was in the past, it's published, otherwise not. I understand that a planned release date can come and go without it actually releasing and the data being updated, but that's still kind of the case, where a Planned release may come (this time it actually gets released) and the data doesn't get updated to Released. But, if you'd prefer to live with the former issue over the latter, this is definitely a better solution. If you prefer the latter, it gets even simpler, since you can just stick with an optional release date.
@zoran-horvat3 ай бұрын
Have you considered tentative analyses? Like querying the library at the end of this year, for the purpose of projecting the revenues? How about doing it backwards, querying the data in the past, so to assess the predicting algorithm on the future that is already past? After a quarter of a century working on enterprise software, I'll tell you there is no business that doesn't ask for auditing and analysis before end. Will you go with "simple" designs for years only to respond with "but we can't implement that"? I witnessed that too many times.
@aredrih67233 ай бұрын
I've made the same assumption as CrapE_DM and I'm really unsure where the disagreement lies. Is it about the fact that an announced book will automatically release if you don't manually update it ? (And lead to misinterpreting the data) If the goal is to allow for retrospectives, then yeah, the date alone won't be enough but both models rely on overriding outdated data to get the up to date state and aren't a great fit for retrospective (you'd lose information on which book were planned to release). IMHO, event sourcing is the gold standard for retrospective and having a talk about data model either suggest you're dealing with aggregates derived from an event stream (and save the stream for eventual replay) or care little about retrospective. Personally, I would take the contrast between announced and released dates as a justification to have 2 date fields rather than reusing one with a boolean flag. (You could even make the case to add a third date field for the date the book enters the library offering)
@zoran-horvat3 ай бұрын
@@aredrih6723 The splitting into two date fields happens naturally when a discriminated union is applied. That opens for another improvement, depending on requirements, as one of the dates might have a different precision than the other, so essentially their underlying types would end up being different.
@JohnSmith-op7ls3 ай бұрын
This is good but I think your examples would be better if they presented any domain specific knowledge or example specific business logic up front. You’re trying to demonstrate why a certain code design is better or worse, if we have to know that a book’s published date doesn’t inherently tell you if it’s been published or not simply by checking if the date is in the past or future, or even null or some default which takes the place of null, then you’re trying to learn and understand some business logic while trying to understand why one solution is better than another. I understand that an example might require some complexity in the scenario to illustrate the point, but trying to explain the code and business logic incrementally, going back and forth between the two, is more complicated than presenting the full problem, then going over the solution. This also comes into play when you ask the audience early on to look at the given code and try to spot the problem. We haven’t been told yet what exactly the code should be doing. So unless we understand how the book publishing business operates, we probably aren’t going to spot issues which require such insight. Also, showing completed code blocks, then describing them is a lot easier to follow than watching characters being incrementally typed, while you’re talking. We read in words and blocks of words, not letter by letter. And our focus doesn’t truly multitask. Presenting information character by character while also trying to process speech makes both a lot harder. Letting us first read the information, then listening to explanation follows the way our focus operates, sequentially. Great topics, just wanted to provide some ideas from a former teacher of 7 years.
@zoran-horvat3 ай бұрын
There is the problem with your proposition when it comes to convincing KZbin visitors to watch the video. However monstrous, the talk must put the cards on the table within the first 20 seconds or they're gone. A video that is structured as an engineer would do, is a certain disaster on KZbin. The algorithm would bury it within an hour. Now about the details. One caught my attention, so I will ask a question now: What is the state of a book with the planned date set, when that date is in the past?
@adambickford87203 ай бұрын
I agree, it feels very bait-and-switch. A boolean was perfectly fine until we increased the complexity of the problem. I subscribe to the exact opposite world view: premature abstraction is far worse than an informed refactoring.
@JohnSmith-op7ls3 ай бұрын
@@zoran-horvat Yes, you have to hook the audience quickly, but once you’ve done that, I think it’s better to provide the context of what’s being solved, then demonstrate how to solve it, then maybe cover problematic ways people try to solve it and why they cause issues. If required information for us to solve the problem on our own is given out incrementally, mixed in with incremental explanation of the solution, we can’t really solve it. It’s like trying to solve a maze while it’s still being drawn. Lead with an enticing hook but then take a more measured approach. I don’t think this will hurt you on YT. Once you get that first 30:seconds, YT counts the view and you can relax the pace. To answer your question: It depends on the business logic. Is a published date only set when the book, is actually published? If it’s set before publishing, can we just change it if the actual date ends up being different, or do we need record what we assumed the date would be even if it was incorrect. If so, two fields would be best, the planned date and actual date. If we don’t care about our initial estimate, just update the date if it changes. If we don’t need to know when it might publish, only when it actually did, leave it a default min date time stamp or whatever your default is (or null). Having two dates provides the most flexibility if in the future you want to see how accurate publish date estimates were and flag projects that were way off. But you can’t get into building out things first hypothetical requirements that never happen, wasting time and increasing technical debt. Or am I misunderstanding the question?
@JohnSmith-op7ls3 ай бұрын
@@adambickford8720 I agree, if accommodating future flexibility is free or requires very little effort, wait until you know you need something. And I mean when dealing with pure hypotheticals, not when the boss says there’s a 75% chance we’ll need to do X in 3-4 years. Odds are even if you know vaguely that you’ll need to accommodate something non-trivial later, whatever you do now will need to be heavily anyhow. Best to build to real requirements later than to guess about them now. Otherwise, where you stop with the guesses, and do you really have time now to accommodate them, much less maintain them if they don’t get used? Probably not.
@adambickford87203 ай бұрын
@@pl4gueis But it wasn't 'proper' until the goalposts were moved. Overengineering is solving problems you 'might' have but don't "yet". Go with the smallest, simplest thing that works. Once you have new *actual* requirements you evolve the design. You may very well end up at a very similar place, but don't pay for it until you need it and know its the proper abstraction. Digging through factories and type hierarchies to find the 1 actual implementation is not 'more proper'. ymmv
@holger_pАй бұрын
Well, I get "don't store something, that can be computed from other properties", that's normal. But I do not see the smallest difference, to using an integer and testing it for 0 and 1 or maybe a third state. Sometimes an enum is better to read, if we are unsure what "true" and "false" actually mean, it shifts the information from the variable name to the value, which is more robust.
@zoran-horvatАй бұрын
@@holger_p The point of the video is in something else, not just in data representation. The problem becomes visible when I ask you where the behavior is implemented in that design.
@holger_pАй бұрын
@@zoran-horvat I don't see anything, that "bool IsPublished => PublishedDate != null" wouldn't solve. And I don't see any difference to using "int IsPublished" and testing "if (IsPublished==1) What you describe is not at all connected to the datatype "bool". Nothing in your video changes, if you replace bool with int or enum. An enum Flag {no, yes} definitly shows the exact same behaviour. I think you want to point out a design problem, but people are confused with a title like "DataType boolean is bad"... without any narrowing context. .NET is full of boolean properties and it cannot be generally bad design, otherwise somebody had already changed it.
@zoran-horvatАй бұрын
@@holger_p It is connected to bool because it soon turned out that there are more than two representations. Aldo, it had to do with bool in a sense that true value comes together with another piece of data and the false value with yet another piece of data. However, the most dramatic deficiency of a bool is in implementing behavior that depends on it, so I will have to repeat the question: Where is that behavior located?
@holger_pАй бұрын
@@zoran-horvat Obviously we live in different worlds. Each "if" requires a bool. World cannot live without bool. You make your video like a quiz show. "Do you see it, Do you see it, Where is it". Over time this style of teaching can become annoying. It makes people feel like an idiot, if they don't see anything. Maybe you refer to code everybody knows. What is wrong with "Rectangle.IsEmpty". Why is this bad design ? Why this is "not my friend". if (rectangle.IsEmpty) if (rectangle.Width==0 && rectangle.Height==0), very same thing, just longer, no difference in design. You want to tell us you write bool-free applications ? That's weird. But that's what the video title suggests. I think there are a lot of side-conditions, under which you want to avoid bools, but you don't speak it out loud.
@zoran-horvatАй бұрын
@@holger_p Wrong answer. Think again: If there is a state represented with a discrete type, such as a bool, then where is the behavior implemented? Stop answering some other questions and focus on this one, because I see a blind spot in you regarding that particular issue.
@TheDiymovies3 ай бұрын
Only if I could come to these realizations on my own as easy as you are able to explain them! Excellent video, gives me much to think about!
@matheosmattsson28113 ай бұрын
Good video! Quite dumb/irrelevant question but here it goes: I have noticed "Modern day C#" code uses more and more type checking using the "is" operator, as you do at the very end. I remember from my days in school, using Java (1.8), that the teacher's strongly discouraged type checking (in Java at least) as it was said to have performance drawbacks as opposed to, I don't know, using a boolean in the parent class to indicate one of two concrete implementations (ironic jokeful example). Is this kind of thinking outdated or was it a myth all along, or could it be that it was something Java specific and has never been a big deal in C#? This springs to mind everytime I typecheck nowdays in C# :P
@zoran-horvat3 ай бұрын
@@matheosmattsson2811 Type checking is quite fast nowadays. It was reimplemented early in the C#'s history and you don't have to fear it in C#. However, type checking is not common in OOP - it is a design flaw more often than not. Type expressions are a native part of functional programming, where it is one of the principal ways of implementing polymorphism. You will note that I am applying type checking to record types only, and not to regular classes in the demo.
@matheosmattsson28113 ай бұрын
@@zoran-horvat Thanks for the answer! How come you only type check for record types? is not the ´record´ type == ´record class` --> syntactic sugar for creating an immutable class with automatic handy implementations for Equals, ToString etc? Why would the "manual" class be discouraged in terms of type checking, if I may ask?
@zoran-horvat3 ай бұрын
@@matheosmattsson2811 That is how they are implemented, but much more is in the intended use. The record is not supposed to contain behavior, only publicly accessible immutable components. From the design point of view, a record is a functional type.
@matheosmattsson28113 ай бұрын
@@zoran-horvat Okay, right got it. Just asking if there was some "hidden" performance related difference which I was missing. Thanks for the clarification
@zoran-horvat3 ай бұрын
@@matheosmattsson2811 My blind guess is that the cost is very small compared to common tagged unions. Performance benefit of tagged unions is that the tag is compile-time constant, and so the compiler can construct an ideal calculated jump. Classes in C#, on the other hand, have a two-byte type identifier. While it is still just a number, it is not known in advance and so a more costly branching is required. All that unless there is some wild optimization in C#, the existence of which I wouldn't dismiss lightly.
@AlFasGD3 ай бұрын
I hope the next one is about tuples not being your friends
@zoran-horvat3 ай бұрын
@@AlFasGD Tuples are your friend. Enums are not.
@adambickford87203 ай бұрын
@@zoran-horvat What's funny is your `PublicationInfo` would be an enum in java.
@zoran-horvat3 ай бұрын
@@adambickford8720 Actually, Java has extended enums to become discriminated unions. C# has records for the same purpose, and will soon attain proper discriminated unions with much cleaner syntax. Rust supports very clean enums with state that look close to the discriminated union syntax proposal in C#. Either way, all those are the same design. The only design that wouldn't work is the C-style enum.
@adambickford87203 ай бұрын
@@zoran-horvat Java has recently added sealed classes for that purpose. afaik enums are the same as they ever were
@tk36_real3 ай бұрын
PublicationDate in the future or null == planned PublicationDate in the past == published way easier…
@zoran-horvat3 ай бұрын
@@tk36_real Planned publication date in the past is still inly a planned publication date. The book will not get published by waiting for some date to pass. It doesn't work that way. Testing for null in dozens and hundreds of places in the calling end is a big red flag for the entire design. It goes the same with testing a bool for true, comparing a date to the current day, and myriad other procedural artefacts. The calling end is not the place to implement domain logic.
@tk36_real3 ай бұрын
@@zoran-horvat a planned publication in the past that didn't go through but is still in the dataset is a problem of validation! and if you think all programmers are too incompetent to handle null being a meaningful value you could a) put a "maximum date" (semantically means indeterminate in the future) or b) provide ONE utility function to wrap it into a polymorphic type like in your video!? you're just making excuses to overcomplicate the API while making the actual date inaccessible…
@zoran-horvat3 ай бұрын
@@tk36_real What do you mean a problem with validation? You cannot alter the data just because you don't like them. We plan it for tomorrow. Two days later the data says we planned it for yesterday. It is a fact, and the database says it.
@tk36_real3 ай бұрын
@@zoran-horvat you cannot plan to release something in the past! if the data is there already you SHOULD alter it to "unspecified future release" and if you need the history put the old planned release date into a database or something
@zoran-horvat3 ай бұрын
@@tk36_real Is it possible that your thought process is so broken? Am I right to suppose you are a senior programmer?
@DovydasGrigaitis3 ай бұрын
This channel is pure gold!
@Ratstail913 ай бұрын
Ha! This reminds me of the cuckoo in a creature-collecting game I made. Because cuckoos in nature are brood parasites, I thought it would be clever to have their eggs obtained in a different fashion... from that point on, suddenly I had to double check in about a dozen places to see if I was dealing with a real egg, or a cuckoo egg in disguise. Whoops.
@zoran-horvat3 ай бұрын
That is the most fragrant point which herds of senior programmers somehow fail to see: the same if branching over and over again, plaguing the entire code base, and for what purpose?
@Ratstail913 ай бұрын
@@zoran-horvat It as only about a dozen files that I had to double check lol. One instance in over 4 years isn't bad.
@dangdudedan87562 ай бұрын
virgin boolean vs integer you only change to 0 and 1
@andreapuerto89673 ай бұрын
Yes he is, I even invited him to my wedding.
@stan_yolo3 ай бұрын
So many pain and cringe. Just use enum.
@zoran-horvat3 ай бұрын
In C# - no. If you thought Rust-style enums, then I already did. So, what's your choice?
@stan_yolo3 ай бұрын
@@zoran-horvat may be I was upset when I write at that comment. Topic of this video is important, but all this was said by F# guys in 2015. From the "informatics" side - all struggles is because you want sum-type, but encoded it by product-type. Old languages hides from programmer the fact that type is a proof by itself. IMO there should be no samples with awful code with defaults, branching on ambiguous semantics etc. Reasoning should be simple: date and flag is highly dependent/coupled, then is must be one type/entity. Whoever wanted to understand - understood. Whoever wanted to learn programming - learnt Rust/F#/Haskel/math/whatever
@LeonardoVargasL3 ай бұрын
No way!!! This is gold! This is poetry! I have no words to describe a magnificent lesson learned in this video. I can see now how to handle any crazy new requirement from a customer/product manager.
@johnwostenberg8403 ай бұрын
Even better yet, use a language with discriminated unions and spell out the various possible states. “Make illegal state unrepresentable”
@chrisjones91323 ай бұрын
I think I walked into the wrong video. I’m a C and asm programmer.
@zoran-horvat3 ай бұрын
@@chrisjones9132 Why do you think this is not applicable there? You would still use structs in C and keep related data together in assembler as well. On a related note, I remember doing dynamic dispatch in assembler with no trouble, whereas C was the testing ground for it in the early days of C++. It is wrong to attribute these techniques to a programming language, in my opinion.
@zoran-horvat3 ай бұрын
@edwardfanboy Don't mix the concept with its implementation. You *will* use a hand-made v-table in assembler because of its conceptual value and utility, despite the potential to err. You can make a myriad of other errors, too, which are detected easily by higher compilers and impossible to make in many other languages. But that potential will still not retire the assembler. It remains useful in its usual niche.
@zoran-horvat3 ай бұрын
@edwardfanboy My point is that if you are working in a language where sum types are not supported natively, you will still not choose to repeat dozens and hundreds of identical branching blocks on the calling end, allow combinatorial explosion of operations, inflate your program by a factor of two or three, and then blame the language for that. I've been working in assembler and in C for years, so I know very well what I am saying.
@facundoayosa80523 ай бұрын
I love your videos, I feel like they ooze in profound knowledge. The thing is I'm too junior to understand 99% of it. I'm fluent in Javascript and barely touch Typescript so you'd understand why. I'll subscribe and read some books until these videos make sense.
@Leftysrev3nge3 ай бұрын
We out here booing Boolean
@zoran-horvat3 ай бұрын
@@Leftysrev3nge Nope.
@kawashirov2 ай бұрын
I simply would do public PublicationDate? ProposedPublicationDate; public PublicationDate? ActualPublicationDate; lol
@zoran-horvat2 ай бұрын
@@kawashirov lol show me the calling end.
@naughtiousmaximus78532 ай бұрын
@@zoran-horvatWouldn't frontend have input field where date for planned publication would be entered and after that some kind of confirmation buttons (PUBLISH/UNPUBLISH for example, just to ilustrate the point) that confirms if book was published when it was planned or not? A POST request for each button can contain book id and if the book was published (PUBLISHED) or not (UNPUBLISHED). Database can for example just have a Id column, Date column and enum with (PUBLISHED/UNPUBLISHED) related to it (BookId: 2167, PlannedPublicationDate: 26.8.2024, PublicationStatus: UNPUBLISHED).
@zoran-horvat2 ай бұрын
@@naughtiousmaximus7853 You have mentioned all parts of the software except the backend. Does the backend exist and what is its role?
@naughtiousmaximus78532 ай бұрын
@@zoran-horvat Maybe in this case backend should just be a layer for data validation and updating? Using something like FluentValidation. Database in this case seems like excellent way to do decision making for this scenario. Basic flow would be frontend -> api endpoint -> validation of data -> service that has dbcontext or repository in constructor -> method that does CRUD is called based on received data -> save changes to the database.
@zoran-horvat2 ай бұрын
@@naughtiousmaximus7853 So you have built yourself a CRUD service. No business rules, no legislation, no nothing. Could be a good school project.
@johndowson18523 ай бұрын
Look at what they need to emulate a fraction of a proper algebraic type.
@zoran-horvat3 ай бұрын
@@johndowson1852 What? It's one line per variant in C# and one line per variant in F#, as well as one line per variant in Rust. You are exaggerating, and that might be on purpose.
@johndowson18523 ай бұрын
@@zoran-horvat yes, I am exaggerating for a comedic effect, but my issue isn't in LOC, it's in the fact that sum types are a closed set, while implementors of an interface is an open state. Which means that while you've made invalid states unrepresentable, you still can introduce a bug by forgetting to handle one of the valid states.
@zoran-horvat3 ай бұрын
@@johndowson1852 I have implemented a sum type with records, not with interfaces.
@Justin-wj4yc2 ай бұрын
@@zoran-horvat you inherited form PublicationInfo. so it's an open set