Boolean Is Not Your Friend

Поділитися
Вставка
  • Опубліковано 18 гру 2024

КОМЕНТАРІ • 374

  • @pl4gueis
    @pl4gueis 4 місяці тому +131

    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.

    • @rethardotv5874
      @rethardotv5874 3 місяці тому +1

      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.

    • @pl4gueis
      @pl4gueis 3 місяці тому +2

      @@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.

    • @rethardotv5874
      @rethardotv5874 3 місяці тому

      @@pl4gueis that’s not what I meant.
      When requirements change the architecture need to change to avoid such nonsense as adding bools.

    • @pl4gueis
      @pl4gueis 3 місяці тому +1

      @@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-h1y
    @cj82-h1y 4 місяці тому +155

    The boolean is not my friend? Well, in all fairness, it's "a bit on again, off again"

    • @zoran-horvat
      @zoran-horvat  4 місяці тому +27

      @@cj82-h1y And when you stumble upon the rock, it's the gravity that made you fall. In all fairness.

    • @KazisCollection
      @KazisCollection 3 місяці тому +1

      Amazing pun

  • @TPInspector
    @TPInspector 4 місяці тому +76

    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-horvat
      @zoran-horvat  4 місяці тому +24

      @@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.

    • @TPInspector
      @TPInspector 4 місяці тому +2

      @@zoran-horvat Oh yeah. I did not even think about that. Thank you :)

    • @fswerneck
      @fswerneck 4 місяці тому +10

      @@zoran-horvat So cool! Discriminated unions is something every language should have!

    • @RandomGeometryDashStuff
      @RandomGeometryDashStuff 4 місяці тому

      hard not to make root class represent invalid state

    • @OatmealTheCrazy
      @OatmealTheCrazy 3 місяці тому +1

      ​@@fswerneck Personally, I'm against apartheid

  • @nickbarton3191
    @nickbarton3191 4 місяці тому +64

    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.

    • @slipperynickels
      @slipperynickels 3 місяці тому +3

      third law is turns out they didn’t need it and it was just a whim from a non-technical manager.

    • @holger_p
      @holger_p 2 місяці тому +1

      It's more like, they are unable to say what they want.

    • @nickbarton3191
      @nickbarton3191 2 місяці тому +1

      @@holger_p Or they explain it in terms of an implementation without describing the problem they need solved.

  • @neeeeeck9005
    @neeeeeck9005 4 місяці тому +26

    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.

    • @Tekner436
      @Tekner436 3 місяці тому

      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

    • @collynchristopherbrenner3245
      @collynchristopherbrenner3245 3 місяці тому

      @@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.

    • @OtakuNoShitpost
      @OtakuNoShitpost 3 місяці тому

      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...

  • @batek34
    @batek34 3 місяці тому +27

    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.

    • @Ownage4lif31
      @Ownage4lif31 3 місяці тому +3

      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.

    • @xpmon
      @xpmon 11 днів тому

      😂

  • @000dr0g
    @000dr0g 4 місяці тому +18

    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.

  • @StefanH
    @StefanH 4 місяці тому +12

    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

  • @fswerneck
    @fswerneck 4 місяці тому +8

    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-horvat
      @zoran-horvat  4 місяці тому +2

      @@fswerneck Sounds similar to my demo example.

    • @fswerneck
      @fswerneck 4 місяці тому

      @@zoran-horvat Shockingly similar! That tells us how common this is.

    • @zoran-horvat
      @zoran-horvat  4 місяці тому +4

      @@fswerneck Oh, there is no mystery there! I made this video because I saw that a million times. And they all look alike.

  • @NullzeRT
    @NullzeRT 4 місяці тому +9

    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-horvat
      @zoran-horvat  4 місяці тому +4

      Rust is doing that very efficiently, both in terms of space-time performance, and in terms of coding effort.

    • @CraigLuna
      @CraigLuna 4 місяці тому +3

      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

  • @yimyim117
    @yimyim117 4 місяці тому +8

    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.

    • @Internetzspacezshipz
      @Internetzspacezshipz 3 місяці тому

      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…

  • @Gremirz
    @Gremirz 4 місяці тому +12

    This OOP approach is just beautiful. You good sir just sold me your course.

  • @chlojolo
    @chlojolo 4 місяці тому +3

    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.

  • @s0psAA
    @s0psAA 4 місяці тому +6

    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-horvat
      @zoran-horvat  4 місяці тому +1

      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.

    • @nanny07
      @nanny07 3 місяці тому

      @@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

  • @markky212
    @markky212 4 місяці тому +3

    Please don't stop your mission Ser!

  • @Vennotius
    @Vennotius 4 місяці тому +3

    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.

  • @MaxPicAxe
    @MaxPicAxe 4 місяці тому +2

    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.

    • @MaxPicAxe
      @MaxPicAxe 4 місяці тому +1

      Edit: seems like that's what you did in the end

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      Yes. C# uses record types for that purpose, and they are really efficient in every respect, including coding time.

  • @MarcoAntonioRosadaSilva-h9c
    @MarcoAntonioRosadaSilva-h9c Місяць тому +1

    I don't know how to explain this, but watching you code feels like watching someone paint.

    • @zoran-horvat
      @zoran-horvat  Місяць тому

      @@MarcoAntonioRosadaSilva-h9c I try to make coding enjoyable.

  • @palapapa0201
    @palapapa0201 4 місяці тому +1

    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.

  • @tryoxiss
    @tryoxiss 4 місяці тому +22

    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-horvat
      @zoran-horvat  4 місяці тому +11

      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.

    • @britbuttmcbooty9221
      @britbuttmcbooty9221 3 місяці тому

      C# has enums I just used them a couple of weeks ago for a final

    • @harrytsang1501
      @harrytsang1501 3 місяці тому

      In C++ we have union class, but the way rust or scala handles it with match case is so much nicer

  • @josebarria3233
    @josebarria3233 4 місяці тому +1

    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

  • @baranacikgoz
    @baranacikgoz 4 місяці тому +1

    That's why I think one must master the strategy pattern before write code. Excellent video.

  • @5cover
    @5cover 4 місяці тому +3

    So satisfying to see that code shrink and simplify!

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      @@5cover Many programmers think that simple state makes simple code. They don't see how complex the methods must be to operate on that.

  • @marcotroster8247
    @marcotroster8247 4 місяці тому +2

    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.

  • @dotnetMasterCSharp
    @dotnetMasterCSharp 10 годин тому

    Most most critical bug I've ever seen, thank you for explanation

  • @dusanknezevic9072
    @dusanknezevic9072 3 місяці тому +4

    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
      @holger_p 2 місяці тому

      I don't see any difference, to use an enum, as a flag. a boolean actually is also an enum.

  • @yuryschkatula9026
    @yuryschkatula9026 4 місяці тому +2

    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-horvat
      @zoran-horvat  4 місяці тому +1

      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!

  • @roll-outcommanders6520
    @roll-outcommanders6520 4 місяці тому +2

    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.

  • @Rick104547
    @Rick104547 4 місяці тому +4

    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-horvat
      @zoran-horvat  4 місяці тому +7

      @@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.

    • @marko5734
      @marko5734 4 місяці тому +1

      You can use ComplexType attribute for value objects

    • @adambickford8720
      @adambickford8720 4 місяці тому

      That's a big downside to this approach; you'll end up writing many 'mappers'.

    • @Rick104547
      @Rick104547 4 місяці тому

      ​@@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-horvat
      @zoran-horvat  4 місяці тому

      @@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?

  • @Art1x_y
    @Art1x_y 4 місяці тому +1

    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-horvat
      @zoran-horvat  4 місяці тому +1

      @@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_Trooper
      @7th_CAV_Trooper 4 місяці тому +3

      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.

  • @NikolozLatsabidze-t6h
    @NikolozLatsabidze-t6h 4 місяці тому +1

    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

  • @ThugLifeModafocah
    @ThugLifeModafocah 3 місяці тому

    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?

  • @NGC-rr6vo
    @NGC-rr6vo 4 місяці тому +5

    damn, thats eye-opening

  • @juliogomez3790
    @juliogomez3790 3 місяці тому +1

    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!

  • @conradrobinson7941
    @conradrobinson7941 3 місяці тому +1

    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.

  • @umdi3337
    @umdi3337 4 місяці тому +1

    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-horvat
      @zoran-horvat  4 місяці тому

      @@umdi3337 Actually, persistence is still not an issue at this level of complexity.

  • @mkwpaul
    @mkwpaul 4 місяці тому +11

    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-horvat
      @zoran-horvat  4 місяці тому +1

      @@mkwpaul I agree with your analysis.

  • @DxCKnew
    @DxCKnew 4 місяці тому +1

    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-horvat
      @zoran-horvat  4 місяці тому +3

      @@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".

  • @gonzo191
    @gonzo191 4 місяці тому +2

    I see what you did there. A response to the confusion from the junior programmer video's book example.

    • @zoran-horvat
      @zoran-horvat  4 місяці тому +1

      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.

  • @rahulmathew8713
    @rahulmathew8713 4 місяці тому +1

    Why are u mixing business layer method and DTO properties in the same class Release. Isnt violation of SOC

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      What do you mean by DTO properties?

    • @rahulmathew8713
      @rahulmathew8713 4 місяці тому

      @@zoran-horvat the Release class we can see properties and methods

  • @2Fast4Youtube
    @2Fast4Youtube 4 місяці тому +1

    So is pretty much:
    If you know you will have more than 2 states, don't use a bool to represent it

    • @zoran-horvat
      @zoran-horvat  4 місяці тому +1

      @@2Fast4UA-cam 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.

  • @NikorouKitsunerou
    @NikorouKitsunerou 4 місяці тому +1

    This might be a point against "code first" databases. Some fields deserve their own tables.

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      I don't think it is big enough to consider it relevant.

  • @user-tk2jy8xr8b
    @user-tk2jy8xr8b 4 місяці тому +1

    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-horvat
      @zoran-horvat  4 місяці тому +2

      @@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-tk2jy8xr8b
      @user-tk2jy8xr8b 4 місяці тому

      @@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-horvat
      @zoran-horvat  4 місяці тому

      @@user-tk2jy8xr8b That is what I said in the video the other way around: a bool is a good answer to a question.

  • @templeofdelusion
    @templeofdelusion 4 місяці тому +1

    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-horvat
      @zoran-horvat  4 місяці тому

      @@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!

    • @RandomGeometryDashStuff
      @RandomGeometryDashStuff 4 місяці тому

      10:16 Planned(date in the past)

  • @felixnotthecat4249
    @felixnotthecat4249 Місяць тому

    how the record Published would be represented in the Database.
    How would I map this to EF?

    • @zoran-horvat
      @zoran-horvat  Місяць тому +1

      @@felixnotthecat4249 You can include them in an entity as a complex property. There are a few later videos where I used that technique.

    • @felixnotthecat4249
      @felixnotthecat4249 Місяць тому +1

      @@zoran-horvat Thanks for answering.

  • @AK-vx4dy
    @AK-vx4dy 3 місяці тому

    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-horvat
      @zoran-horvat  3 місяці тому

      @@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-vx4dy
      @AK-vx4dy 3 місяці тому

      @@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.

  • @ParkourGrip
    @ParkourGrip 3 місяці тому

    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-horvat
      @zoran-horvat  3 місяці тому

      @@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.

  • @rotamrofsnart
    @rotamrofsnart 4 місяці тому +1

    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-horvat
      @zoran-horvat  4 місяці тому

      There are many books that would help you: Martin Fowler, Eric Evans, Bertrand Meyer, etc.

  • @IAsyncEnumerable
    @IAsyncEnumerable 4 місяці тому +1

    At 0:52, the problem was actually just created. Having two different variables to stress one thing. A book that is published will definitely have the date, so no need for the bool variable in the first place.

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      @@IAsyncEnumerable Nope. Think again.

    • @IAsyncEnumerable
      @IAsyncEnumerable 4 місяці тому +1

      @@zoran-horvat Can't see other way around. Please help me out? :)

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      @@IAsyncEnumerable What about the book that is only planned to be published? That is what bool used to discriminate.

    • @IAsyncEnumerable
      @IAsyncEnumerable 3 місяці тому +2

      @@zoran-horvat Now, I understand. If I limit myself to the context of 0:52, I would have two datetime fields. PlannedPublishDate and PublishDate. Of course, that is, if the requirement is known at that point that planned publish date will be needed. The reason I am so stuck at the context around 0:52 is that the, even though it is an example, I would rather be looking to your root cause that is being mocked. Thanks for the reply.

  • @alex38235
    @alex38235 3 місяці тому

    This basically acomplishes the same thing as the typestate pattern right?

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      @@alex38235 Yes, it looks very similar. That is the coding pattern known in all languages and coding styles, primarily for its safety and clarity.

  • @yurisich
    @yurisich 3 місяці тому

    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-horvat
      @zoran-horvat  3 місяці тому

      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.

  • @fennecbesixdouze1794
    @fennecbesixdouze1794 4 місяці тому +1

    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-horvat
      @zoran-horvat  4 місяці тому +2

      @@fennecbesixdouze1794 The suggested method fails at runtime. I don't like having code that fails. I prefer code that works with no failure.

  • @DavidSmith-ef4eh
    @DavidSmith-ef4eh 4 місяці тому +5

    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-ef4eh
      @DavidSmith-ef4eh 4 місяці тому

      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..

    • @pl4gueis
      @pl4gueis 4 місяці тому

      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-ef4eh
      @DavidSmith-ef4eh 4 місяці тому +1

      @@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.

    • @pl4gueis
      @pl4gueis 4 місяці тому

      @@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-horvat
      @zoran-horvat  4 місяці тому +2

      @@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.

  • @perplexedon9834
    @perplexedon9834 3 місяці тому

    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-horvat
      @zoran-horvat  3 місяці тому

      @@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.

  • @shadeblackwolf1508
    @shadeblackwolf1508 4 місяці тому +1

    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-horvat
      @zoran-horvat  4 місяці тому

      @@shadeblackwolf1508 On most occasions, a bool is a good answer to a question, but an incomplete representation of the state.

    • @ferinzz
      @ferinzz 3 місяці тому

      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.

  • @trustytrojan
    @trustytrojan 4 місяці тому +1

    why add a boolean when you can simply check whether the PublicationDate has passed?

    • @zoran-horvat
      @zoran-horvat  4 місяці тому +4

      Because it can pass without the book actually being published. Life is sometimes like that.

  • @jakezepeda1267
    @jakezepeda1267 3 місяці тому

    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-horvat
      @zoran-horvat  3 місяці тому

      @@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.

  • @fallegapyro
    @fallegapyro 4 місяці тому +1

    Everyday, something new is "not my friend"! Hell nah! Strings, booleans, singletons and others are my friends!

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      @@fallegapyro How do you contain code duplication and code inflation, then? Looks like you are stuck with the data model, but somehow ignore behavior.

  • @twenty-fifth420
    @twenty-fifth420 4 місяці тому +1

    I dont use Booleans, I just use 0 and 1 and I make sure the compiler doesn’t forget it. 🔫🔫

  • @unexpectedkAs
    @unexpectedkAs 4 місяці тому +1

    Really good video, really good examples and step by step presentation. Will be sharing it a lot!

  • @Merssedes
    @Merssedes 3 місяці тому +1

    After watching this video, I've one question: why so many classes?..

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      @@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?

    • @Merssedes
      @Merssedes 3 місяці тому

      @@zoran-horvat What model are we talking about? Because my knowledge of C# does not contain such term.

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      @@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.

    • @Merssedes
      @Merssedes 3 місяці тому +1

      @@zoran-horvat So all it means is that I just missing context for the video. Because i've never heard term "domain model" before.

  • @gregorymorse8423
    @gregorymorse8423 4 місяці тому +2

    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-horvat
      @zoran-horvat  4 місяці тому

      Why would I talk the same to a BIOS company as to business application developers? Do I look like an idiot?

    • @gregorymorse8423
      @gregorymorse8423 4 місяці тому

      @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-horvat
      @zoran-horvat  4 місяці тому

      @@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.

  • @AndersBaumann
    @AndersBaumann 4 місяці тому +1

    This refactoring looks jolly good until it is time to persist the Release class and PublicationInfo record with EF core or NHibernate.

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      @@AndersBaumann Actually, it is quite straightforward with EF Core.

    • @AndersBaumann
      @AndersBaumann 4 місяці тому

      @@zoran-horvat May we see that?

    • @zoran-horvat
      @zoran-horvat  4 місяці тому +1

      @@AndersBaumann There are a couple older videos with full persistence of this model, and there will be a few more to come.

    • @AndersBaumann
      @AndersBaumann 4 місяці тому

      @@zoran-horvat What are the names of the older videos so I can find them?

  • @impero101
    @impero101 3 місяці тому

    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-horvat
      @zoran-horvat  3 місяці тому +1

      @@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.

  • @sf-petru
    @sf-petru 2 місяці тому

    this is a nightmare to change when the logic is huge and the customer wants big changes

    • @zoran-horvat
      @zoran-horvat  2 місяці тому

      @@sf-petru Do you have an example of a "big change" that would cause troubles? I see irrational fear behind your words.

    • @sf-petru
      @sf-petru 2 місяці тому

      ​ @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-horvat
      @zoran-horvat  2 місяці тому +1

      @@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-petru
      @sf-petru 2 місяці тому

      @@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-petru
      @sf-petru 2 місяці тому

      @@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

  • @yeti25934
    @yeti25934 4 місяці тому +10

    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.

    • @aflous
      @aflous 3 місяці тому +4

      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.

  • @sorakatadzuma8044
    @sorakatadzuma8044 3 місяці тому

    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-horvat
      @zoran-horvat  3 місяці тому +1

      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.

  • @SteinGauslaaStrindhaug
    @SteinGauslaaStrindhaug 3 місяці тому

    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-horvat
      @zoran-horvat  3 місяці тому

      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.

    • @SteinGauslaaStrindhaug
      @SteinGauslaaStrindhaug 3 місяці тому

      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-horvat
      @zoran-horvat  3 місяці тому

      @@SteinGauslaaStrindhaug Where do you implement a domain operation when the state is represented by a primitive type?

  • @Foxsterdota
    @Foxsterdota 4 місяці тому

    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-horvat
      @zoran-horvat  4 місяці тому +1

      @@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.

  • @makemeafirewall
    @makemeafirewall 4 місяці тому

    This is great, please explain how to use this with ef core, how should we use the polimorphic publishing state in the DB?

    • @zoran-horvat
      @zoran-horvat  4 місяці тому +1

      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.

  • @JohnSmith-op7ls
    @JohnSmith-op7ls 4 місяці тому +2

    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-horvat
      @zoran-horvat  4 місяці тому +1

      There is the problem with your proposition when it comes to convincing UA-cam 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 UA-cam. 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?

    • @adambickford8720
      @adambickford8720 4 місяці тому

      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-op7ls
      @JohnSmith-op7ls 4 місяці тому

      @@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-op7ls
      @JohnSmith-op7ls 4 місяці тому

      @@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.

    • @adambickford8720
      @adambickford8720 4 місяці тому

      @@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

  • @timseguine2
    @timseguine2 3 місяці тому

    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.

  • @mohammadtoficmohammad3594
    @mohammadtoficmohammad3594 3 місяці тому +1

    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

  • @CrapE_DM
    @CrapE_DM 4 місяці тому

    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-horvat
      @zoran-horvat  4 місяці тому

      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.

    • @aredrih6723
      @aredrih6723 4 місяці тому +1

      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-horvat
      @zoran-horvat  4 місяці тому

      @@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.

  • @NGC-rr6vo
    @NGC-rr6vo 4 місяці тому

    hi, what type of content do you have on patreon? may be there is table of content somewhere?

    • @zoran-horvat
      @zoran-horvat  4 місяці тому +1

      @@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.

  • @johanavril1691
    @johanavril1691 4 місяці тому +1

    Today I learn that c# doesnt have tagged unions

    • @zoran-horvat
      @zoran-horvat  4 місяці тому +1

      But it has record types with a tag (sic!).

  • @jshowao
    @jshowao 3 місяці тому

    It doesnt even make sense to do this when you have a date published. If it was never published, shouldnt there be no date present?

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      Define no date present.
      The requirements recognize two dates: publication date, and planned publication date. A publication date is indicating a past event, but could as well be in the future, if you know what I mean. The planned publication date is genuinely future, but may slip into the past if nobody does something about it.
      I expect you to give that requirements analysis a second chance before replying.

    • @jshowao
      @jshowao 3 місяці тому

      @@zoran-horvat There is no logic to a publication date occurring in the future, especially if there is a planned publication date. Publication date, to me, is when a book is actually published while a planned one can have a future date.

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      @@jshowao Define future.

    • @jshowao
      @jshowao 3 місяці тому

      @@zoran-horvat Okay, future as in time that has not come about but will?

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      @@jshowao In programming, not in The Lord of the Rings.

  • @holger_p
    @holger_p 2 місяці тому

    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
      @zoran-horvat  2 місяці тому

      @@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
      @holger_p 2 місяці тому

      @@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
      @zoran-horvat  2 місяці тому

      @@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
      @holger_p 2 місяці тому

      @@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
      @zoran-horvat  2 місяці тому

      @@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.

  • @lukkkasz323
    @lukkkasz323 4 місяці тому

    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-horvat
      @zoran-horvat  4 місяці тому +1

      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.

  • @matheosmattsson2811
    @matheosmattsson2811 4 місяці тому

    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-horvat
      @zoran-horvat  4 місяці тому +1

      @@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.

    • @matheosmattsson2811
      @matheosmattsson2811 4 місяці тому

      @@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-horvat
      @zoran-horvat  4 місяці тому +1

      @@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.

    • @matheosmattsson2811
      @matheosmattsson2811 4 місяці тому

      @@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-horvat
      @zoran-horvat  4 місяці тому +1

      @@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.

  • @MrCumberlander1
    @MrCumberlander1 3 місяці тому +1

    Rust enums would be perfect for this purpose.

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      @@MrCumberlander1 Rust enums are identical to C# records I used in the video. Same thing, different language.

  • @HkanAktas
    @HkanAktas 4 місяці тому +1

    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-horvat
      @zoran-horvat  4 місяці тому

      @@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?

    • @Mystic998
      @Mystic998 4 місяці тому

      @@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.

    • @HkanAktas
      @HkanAktas 4 місяці тому

      @@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-horvat
      @zoran-horvat  4 місяці тому

      @@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.

    • @HkanAktas
      @HkanAktas 3 місяці тому

      @@zoran-horvat Ugh, I wrote a detailed answer the same day, but either my internet failed me or UA-cam 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.

  • @RoboDragonJediKnight
    @RoboDragonJediKnight 3 місяці тому

    Alternatively, could be solved with some finite state machine patterns. A good section in "The Pragmatic Programmer" on this pattern.

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      @@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.

  • @zsoltesse987
    @zsoltesse987 4 місяці тому

    @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-horvat
      @zoran-horvat  4 місяці тому +1

      @@zsoltesse987 I'm changing my style all the time. The points you make are valuable, thank you.

  • @Mothuzad
    @Mothuzad 4 місяці тому

    > 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-horvat
      @zoran-horvat  4 місяці тому

      @@Mothuzad Did you see any rage in the comments? I see none.
      P.S. I may have not understood all too well.

  • @colinmaharaj
    @colinmaharaj 3 місяці тому

    I'm a C guy, there are bool and BOOL, I use both.

  • @Karloffspring
    @Karloffspring 3 місяці тому +4

    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.)

  • @v0id_d3m0n
    @v0id_d3m0n 2 місяці тому +1

    Wow - it's beautiful!

  • @andreapuerto8967
    @andreapuerto8967 3 місяці тому

    Yes he is, I even invited him to my wedding.

  • @tk36_real
    @tk36_real 3 місяці тому +1

    PublicationDate in the future or null == planned
    PublicationDate in the past == published
    way easier…

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      @@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_real
      @tk36_real 3 місяці тому

      @@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-horvat
      @zoran-horvat  3 місяці тому

      @@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_real
      @tk36_real 3 місяці тому

      @@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-horvat
      @zoran-horvat  3 місяці тому

      @@tk36_real Is it possible that your thought process is so broken? Am I right to suppose you are a senior programmer?

  • @AlFasGD
    @AlFasGD 4 місяці тому

    I hope the next one is about tuples not being your friends

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      @@AlFasGD Tuples are your friend. Enums are not.

    • @adambickford8720
      @adambickford8720 4 місяці тому

      @@zoran-horvat What's funny is your `PublicationInfo` would be an enum in java.

    • @zoran-horvat
      @zoran-horvat  4 місяці тому +2

      @@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.

    • @adambickford8720
      @adambickford8720 4 місяці тому

      @@zoran-horvat Java has recently added sealed classes for that purpose. afaik enums are the same as they ever were

  • @sebastianxx3687
    @sebastianxx3687 4 місяці тому +1

    Hi Zoran, is there anywhere your course dedicated to ddd ?

    • @zoran-horvat
      @zoran-horvat  4 місяці тому +2

      @@sebastianxx3687 No, I have no plans to make a full course on DDD in the near future. However, I plan to insert a chapter or a few clips every now and then to explain what is usually referred to as tactical DDD - patterns such as subdomains, bounded contexts, aggregates, and the like. I find great value in those.

  • @johndowson1852
    @johndowson1852 3 місяці тому

    Look at what they need to emulate a fraction of a proper algebraic type.

    • @zoran-horvat
      @zoran-horvat  3 місяці тому

      @@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.

    • @johndowson1852
      @johndowson1852 3 місяці тому

      @@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-horvat
      @zoran-horvat  3 місяці тому

      @@johndowson1852 I have implemented a sum type with records, not with interfaces.

    • @Justin-wj4yc
      @Justin-wj4yc 3 місяці тому

      @@zoran-horvat you inherited form PublicationInfo. so it's an open set

  • @dangdudedan8756
    @dangdudedan8756 3 місяці тому

    virgin boolean vs integer you only change to 0 and 1

  • @Ratstail91
    @Ratstail91 4 місяці тому

    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-horvat
      @zoran-horvat  4 місяці тому

      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?

    • @Ratstail91
      @Ratstail91 4 місяці тому +1

      @@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.

  • @DovydasGrigaitis
    @DovydasGrigaitis 4 місяці тому +1

    This channel is pure gold!

  • @chrisjones9132
    @chrisjones9132 3 місяці тому

    I think I walked into the wrong video. I’m a C and asm programmer.

    • @zoran-horvat
      @zoran-horvat  3 місяці тому +1

      @@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-horvat
      @zoran-horvat  3 місяці тому +1

      @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-horvat
      @zoran-horvat  3 місяці тому +1

      @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.

  • @johnwostenberg840
    @johnwostenberg840 3 місяці тому

    Even better yet, use a language with discriminated unions and spell out the various possible states. “Make illegal state unrepresentable”

  • @weluvmusicz
    @weluvmusicz 4 місяці тому

    Now show us how to handle this Relase class with EF Core :D

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      @@weluvmusicz It is immutable. EF Core is out already because it doesn't operate on immutable models, unfortunately.

    • @joostleisink3431
      @joostleisink3431 4 місяці тому

      You could create separate entity type(s) for that in you infrastructure layer. There, you also handle the mapping from your rich domain object to your entity type (vice-versa) and let EF Core do the rest.

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      @@joostleisink3431 It is possible to attach convenient extension methods to a DbContext to handle immutable models the way we do in functional applications.

    • @krss6256
      @krss6256 4 місяці тому

      @@zoran-horvat Actually I can't see in your code anything that would prevent EF Core 8 from working with it if you configure it properly.
      This is hugely requested topic always in your comments and I can't wait to also see a video about EF Core + DDD's rich domain model with some of these classes.
      In this video I think it should be fairly easy to use EF Core. You are sometimes showing more complicated cases where I'm unsure if that would work but here I think it should easily?

    • @zoran-horvat
      @zoran-horvat  4 місяці тому

      @@krss6256 There are a dozen of earlier videos demonstrating other designs on the same bookstore model, that included persistence with EF Core. There is really nothing in this model yet, that would cause complications or require tradeoffs when EF Core is used.

  • @Leftysrev3nge
    @Leftysrev3nge 3 місяці тому

    We out here booing Boolean