Boolean Is Not Your Friend

Поделиться
HTML-код
  • Опубликовано: 26 окт 2024

Комментарии • 369

  • @pl4gueis
    @pl4gueis 2 месяца назад +129

    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 2 месяца назад +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 2 месяца назад +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 2 месяца назад

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

    • @pl4gueis
      @pl4gueis 2 месяца назад +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 2 месяца назад +151

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад +25

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

    • @KazisCollection
      @KazisCollection Месяц назад +1

      Amazing pun

  • @milanvasilic4510
    @milanvasilic4510 2 месяца назад +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  2 месяца назад +24

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

    • @milanvasilic4510
      @milanvasilic4510 2 месяца назад +2

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

    • @fswerneck
      @fswerneck 2 месяца назад +10

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

    • @RandomGeometryDashStuff
      @RandomGeometryDashStuff 2 месяца назад

      hard not to make root class represent invalid state

    • @OatmealTheCrazy
      @OatmealTheCrazy 2 месяца назад +1

      ​@@fswerneck Personally, I'm against apartheid

  • @nickbarton3191
    @nickbarton3191 2 месяца назад +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 Месяц назад +2

      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 25 дней назад +1

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

    • @nickbarton3191
      @nickbarton3191 25 дней назад +1

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

  • @neeeeeck9005
    @neeeeeck9005 2 месяца назад +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 2 месяца назад

      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 2 месяца назад

      @@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 Месяц назад

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

  • @000dr0g
    @000dr0g 2 месяца назад +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.

  • @fswerneck
    @fswerneck 2 месяца назад +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  2 месяца назад +2

      @@fswerneck Sounds similar to my demo example.

    • @fswerneck
      @fswerneck 2 месяца назад

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад +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 2 месяца назад +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  2 месяца назад +4

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

    • @CraigLuna
      @CraigLuna 2 месяца назад +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

  • @StefanH
    @StefanH 2 месяца назад +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

  • @batek34
    @batek34 Месяц назад +23

    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

      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.

  • @yimyim117
    @yimyim117 2 месяца назад +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 2 месяца назад

      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 2 месяца назад +12

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

  • @s0psAA
    @s0psAA 2 месяца назад +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  2 месяца назад +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 Месяц назад

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

  • @tryoxiss
    @tryoxiss 2 месяца назад +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  2 месяца назад +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 2 месяца назад

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

    • @harrytsang1501
      @harrytsang1501 2 месяца назад

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

  • @chlojolo
    @chlojolo 2 месяца назад +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.

  • @yeti25934
    @yeti25934 2 месяца назад +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 2 месяца назад +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.

  • @MaxPicAxe
    @MaxPicAxe 2 месяца назад +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 2 месяца назад +1

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

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

  • @markky212
    @markky212 2 месяца назад +3

    Please don't stop your mission Ser!

  • @juliogomez3790
    @juliogomez3790 2 месяца назад +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!

  • @Vennotius
    @Vennotius 2 месяца назад +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.

  • @marcotroster8247
    @marcotroster8247 2 месяца назад +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.

  • @mkwpaul
    @mkwpaul 2 месяца назад +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  2 месяца назад +1

      @@mkwpaul I agree with your analysis.

  • @josebarria3233
    @josebarria3233 2 месяца назад +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

  • @dusanknezevic9072
    @dusanknezevic9072 2 месяца назад +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 25 дней назад

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

  • @DxCKnew
    @DxCKnew 2 месяца назад +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  2 месяца назад +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".

  • @baranacikgoz
    @baranacikgoz 2 месяца назад +1

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

  • @yuryschkatula9026
    @yuryschkatula9026 2 месяца назад +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  2 месяца назад +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!

  • @5cover
    @5cover 2 месяца назад +3

    So satisfying to see that code shrink and simplify!

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

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

  • @roll-outcommanders6520
    @roll-outcommanders6520 2 месяца назад +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.

  • @2Fast4Youtube
    @2Fast4Youtube 2 месяца назад +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  2 месяца назад +1

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

  • @Art1x_y
    @Art1x_y 2 месяца назад +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  2 месяца назад +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 2 месяца назад +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.

  • @fennecbesixdouze1794
    @fennecbesixdouze1794 2 месяца назад +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  2 месяца назад +2

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

  • @conradrobinson7941
    @conradrobinson7941 2 месяца назад +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 2 месяца назад +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  2 месяца назад

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

  • @yurisich
    @yurisich 2 месяца назад

    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  2 месяца назад

      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.

  • @fallegapyro
    @fallegapyro 2 месяца назад +1

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

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

  • @NGC-rr6vo
    @NGC-rr6vo 2 месяца назад +5

    damn, thats eye-opening

  • @palapapa0201
    @palapapa0201 2 месяца назад +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.

  • @Rick104547
    @Rick104547 2 месяца назад +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  2 месяца назад +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 2 месяца назад +1

      You can use ComplexType attribute for value objects

    • @adambickford8720
      @adambickford8720 2 месяца назад

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

    • @Rick104547
      @Rick104547 2 месяца назад

      ​@@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  2 месяца назад

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

  • @user-tk2jy8xr8b
    @user-tk2jy8xr8b 2 месяца назад +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  2 месяца назад +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 2 месяца назад

      @@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  2 месяца назад

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

  • @NikorouKitsunerou
    @NikorouKitsunerou 2 месяца назад +1

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

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

  • @NikolozLatsabidze-t6h
    @NikolozLatsabidze-t6h 2 месяца назад +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 Месяц назад

    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?

  • @gonzo191
    @gonzo191 2 месяца назад +2

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад +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.

  • @trustytrojan
    @trustytrojan 2 месяца назад +1

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад +4

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

  • @rotamrofsnart
    @rotamrofsnart 2 месяца назад +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  2 месяца назад

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

  • @sorakatadzuma8044
    @sorakatadzuma8044 2 месяца назад

    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  2 месяца назад +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.

  • @templeofdelusion
    @templeofdelusion 2 месяца назад +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  2 месяца назад

      @@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 2 месяца назад

      10:16 Planned(date in the past)

  • @unexpectedkAs
    @unexpectedkAs 2 месяца назад +1

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

  • @perplexedon9834
    @perplexedon9834 2 месяца назад

    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  2 месяца назад

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

  • @DavidSmith-ef4eh
    @DavidSmith-ef4eh 2 месяца назад +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 2 месяца назад

      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 2 месяца назад

      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 2 месяца назад +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 2 месяца назад

      @@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  2 месяца назад +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.

  • @ParkourGrip
    @ParkourGrip 2 месяца назад

    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  2 месяца назад

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

  • @Merssedes
    @Merssedes Месяц назад +1

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

    • @zoran-horvat
      @zoran-horvat  Месяц назад

      @@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 Месяц назад

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

    • @zoran-horvat
      @zoran-horvat  Месяц назад

      @@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 Месяц назад +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.

  • @jakezepeda1267
    @jakezepeda1267 2 месяца назад

    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  2 месяца назад

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

  • @Foxsterdota
    @Foxsterdota 2 месяца назад

    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  2 месяца назад +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.

  • @timseguine2
    @timseguine2 2 месяца назад

    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.

  • @impero101
    @impero101 2 месяца назад

    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  2 месяца назад +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.

  • @shadeblackwolf1508
    @shadeblackwolf1508 2 месяца назад +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  2 месяца назад

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

    • @ferinzz
      @ferinzz 2 месяца назад

      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.

  • @AK-vx4dy
    @AK-vx4dy Месяц назад

    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  Месяц назад

      @@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 Месяц назад

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

  • @twenty-fifth420
    @twenty-fifth420 2 месяца назад +1

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

  • @RoboDragonJediKnight
    @RoboDragonJediKnight Месяц назад

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

    • @zoran-horvat
      @zoran-horvat  Месяц назад

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

  • @SteinGauslaaStrindhaug
    @SteinGauslaaStrindhaug Месяц назад

    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  Месяц назад

      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 Месяц назад

      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  Месяц назад

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

  • @AndersBaumann
    @AndersBaumann 2 месяца назад +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  2 месяца назад

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

    • @AndersBaumann
      @AndersBaumann 2 месяца назад

      @@zoran-horvat May we see that?

    • @zoran-horvat
      @zoran-horvat  2 месяца назад +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 2 месяца назад

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

  • @rahulmathew8713
    @rahulmathew8713 2 месяца назад +1

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

      What do you mean by DTO properties?

    • @rahulmathew8713
      @rahulmathew8713 2 месяца назад

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

  • @gregorymorse8423
    @gregorymorse8423 2 месяца назад +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  2 месяца назад

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

    • @gregorymorse8423
      @gregorymorse8423 2 месяца назад

      @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  2 месяца назад

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

  • @Karloffspring
    @Karloffspring Месяц назад +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.)

  • @lukkkasz323
    @lukkkasz323 2 месяца назад

    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  2 месяца назад +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.

  • @CrapE_DM
    @CrapE_DM 2 месяца назад

    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  2 месяца назад

      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 2 месяца назад +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  2 месяца назад

      @@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 2 месяца назад

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад +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.

  • @IAsyncEnumerable
    @IAsyncEnumerable 2 месяца назад +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  2 месяца назад

      @@IAsyncEnumerable Nope. Think again.

    • @IAsyncEnumerable
      @IAsyncEnumerable 2 месяца назад +1

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

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

    • @IAsyncEnumerable
      @IAsyncEnumerable 2 месяца назад +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.

  • @holger_p
    @holger_p 25 дней назад

    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  25 дней назад

      @@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 25 дней назад

      @@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  25 дней назад

      @@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 25 дней назад

      @@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  25 дней назад

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

  • @sf-petru
    @sf-petru Месяц назад

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

    • @zoran-horvat
      @zoran-horvat  Месяц назад

      @@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 Месяц назад

      ​ @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  Месяц назад +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 Месяц назад

      @@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 Месяц назад

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

  • @alex38235
    @alex38235 Месяц назад

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

    • @zoran-horvat
      @zoran-horvat  Месяц назад

      @@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
    @felixnotthecat4249 2 дня назад

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

    • @zoran-horvat
      @zoran-horvat  2 дня назад

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

  • @MrCumberlander1
    @MrCumberlander1 2 месяца назад +1

    Rust enums would be perfect for this purpose.

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

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

  • @johanavril1691
    @johanavril1691 2 месяца назад +1

    Today I learn that c# doesnt have tagged unions

    • @zoran-horvat
      @zoran-horvat  2 месяца назад +1

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

  • @mohammadtoficmohammad3594
    @mohammadtoficmohammad3594 2 месяца назад +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

  • @zsoltesse987
    @zsoltesse987 2 месяца назад

    @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  2 месяца назад +1

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

  • @jshowao
    @jshowao Месяц назад

    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  Месяц назад

      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 Месяц назад

      @@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  Месяц назад

      @@jshowao Define future.

    • @jshowao
      @jshowao Месяц назад

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

    • @zoran-horvat
      @zoran-horvat  Месяц назад

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

  • @JohnSmith-op7ls
    @JohnSmith-op7ls 2 месяца назад +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  2 месяца назад +1

      There is the problem with your proposition when it comes to convincing RUclips 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 RUclips. 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 2 месяца назад

      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 2 месяца назад

      @@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 2 месяца назад

      @@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 2 месяца назад

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

  • @matheosmattsson2811
    @matheosmattsson2811 2 месяца назад

    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  2 месяца назад +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 2 месяца назад

      @@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  2 месяца назад +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 2 месяца назад

      @@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  2 месяца назад +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.

  • @colinmaharaj
    @colinmaharaj Месяц назад

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

  • @Ratstail91
    @Ratstail91 2 месяца назад

    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  2 месяца назад

      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 2 месяца назад +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.

  • @makemeafirewall
    @makemeafirewall 2 месяца назад

    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  2 месяца назад +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.

  • @HkanAktas
    @HkanAktas 2 месяца назад +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  2 месяца назад

      @@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 2 месяца назад

      @@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 2 месяца назад

      @@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  2 месяца назад

      @@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 2 месяца назад

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

  • @johndowson1852
    @johndowson1852 2 месяца назад

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

      @@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 2 месяца назад

      @@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  2 месяца назад

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

    • @Justin-wj4yc
      @Justin-wj4yc Месяц назад

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

  • @AlFasGD
    @AlFasGD 2 месяца назад

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

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

    • @adambickford8720
      @adambickford8720 2 месяца назад

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад +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 2 месяца назад

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

  • @tk36_real
    @tk36_real 2 месяца назад +1

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

      @@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 2 месяца назад

      @@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  2 месяца назад

      @@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 2 месяца назад

      @@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  2 месяца назад

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

  • @v0id_d3m0n
    @v0id_d3m0n Месяц назад +1

    Wow - it's beautiful!

  • @dangdudedan8756
    @dangdudedan8756 Месяц назад

    virgin boolean vs integer you only change to 0 and 1

  • @Mothuzad
    @Mothuzad 2 месяца назад

    > 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  2 месяца назад

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

  • @kawashirov
    @kawashirov Месяц назад +1

    I simply would do
    public PublicationDate? ProposedPublicationDate;
    public PublicationDate? ActualPublicationDate;
    lol

    • @zoran-horvat
      @zoran-horvat  Месяц назад

      @@kawashirov lol show me the calling end.

    • @naughtiousmaximus7853
      @naughtiousmaximus7853 Месяц назад

      ​@@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-horvat
      @zoran-horvat  Месяц назад

      @@naughtiousmaximus7853 You have mentioned all parts of the software except the backend. Does the backend exist and what is its role?

    • @naughtiousmaximus7853
      @naughtiousmaximus7853 Месяц назад

      @@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-horvat
      @zoran-horvat  Месяц назад

      @@naughtiousmaximus7853 So you have built yourself a CRUD service. No business rules, no legislation, no nothing. Could be a good school project.

  • @trubessinum
    @trubessinum 2 месяца назад +1

    The root of such an issue is in unsanitized requirements. There should've been no implementation to begin with until both requirements were considered properly and specific design was chosen to meet them. Clear communication of demands is key.

    • @zoran-horvat
      @zoran-horvat  2 месяца назад +1

      @@trubessinum Great. Here's the deal: The first requirement was received in January, the second in August.
      Your turn now.

    • @trubessinum
      @trubessinum 2 месяца назад +1

      @@zoran-horvat I don't see how we disagree. My point is that it should not take much time to help the customer better understand their needs and choose a better design early. If I understand you correctly, the idea is to not choose a bool in this situation even if there are still only two states. That being said, in a given scenario the existence of additional state would be revealed and considered if all the roles did their job properly and were more active in the process of communication.

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

      @@trubessinum How can someone do their job properly in the future?

  • @andreapuerto8967
    @andreapuerto8967 2 месяца назад

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

  • @omegahaxors9-11
    @omegahaxors9-11 Месяц назад

    The main reason not to use bool is that your computer is REALLY bad at handling them. You need a whole byte to store a single bit of information. They're internally shorts with only two values, with the compiler optimizing bool arrays nearby into a single coded short.
    Here's how it decodes a bool: it takes the byte and then shifts off bits until only the last one remains. In the case of an array it will AND it with a bitmap to filter out irrelevant bits then do the shifting again.
    The only acceptable place for bools is in large arrays for example a gameboy save file or when used in functions for example the JumpIF opcode.

  • @qexat
    @qexat 2 месяца назад

    "Make invalid states unrepresentable"

  • @chrisjones9132
    @chrisjones9132 2 месяца назад

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад +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  2 месяца назад +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  2 месяца назад +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.

  • @DovydasGrigaitis
    @DovydasGrigaitis 2 месяца назад +1

    This channel is pure gold!

  • @weluvmusicz
    @weluvmusicz 2 месяца назад

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

    • @zoran-horvat
      @zoran-horvat  2 месяца назад

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

    • @joostleisink3431
      @joostleisink3431 2 месяца назад

      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  2 месяца назад

      @@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 2 месяца назад

      @@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  2 месяца назад

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

  • @LeonardoVargasL
    @LeonardoVargasL 2 месяца назад +1

    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.