How Strongly Typed IDs Can Make Your Code More Expressive | DDD

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

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

  • @MilanJovanovicTech
    @MilanJovanovicTech  Год назад +11

    Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
    Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt

  • @rahulaga
    @rahulaga Год назад +4

    you truly stand out differently by giving practical examples..and during the course we got to know many other new things as side effect 😊

  • @feefifofum6383
    @feefifofum6383 Год назад +11

    Love it. Could you do a follow up showing exactly how to integrate it with efcore…. That would be great.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +8

      Yes, the EF Core + DDD Mapping video is coming out next Friday :)

    • @krccmsitp2884
      @krccmsitp2884 Год назад

      @@MilanJovanovicTech Great! I'm looking forward to it.

  • @marna_li
    @marna_li Год назад +26

    A tip: You can implement IParsable so you can use these strongly typed Ids for arg in your controllers and endpoints.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +7

      I would honestly leave them out of the controller endpoints and just use primitive types, but that's just my opinion

    • @marna_li
      @marna_li Год назад +2

      @@MilanJovanovicTech I actually find it neat if you see the Ids as “guarded types or values” because you get validation wherever the values first get created, like in controllers.

    • @modernkennnern
      @modernkennnern Год назад +6

      @@MilanJovanovicTech I feel like if you don't do it _everywhere_ - to the point it's almost difficult to know what it's representing - it loses a lot of it's power

  • @pete9049
    @pete9049 Год назад +2

    I came here to know more about CLEAN architecture tutorials but I guess I have to start watching first the DDD playlist. This really helps me a lot and will definitely give you a tip on my next payroll 💯

  • @sunnypatel1045
    @sunnypatel1045 Год назад +2

    Love your videos keep up with them! I be keen to see your take on testing strategies within your DDD projects.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Unit testing DDD is pretty easy, don't you think?

    • @sunnypatel1045
      @sunnypatel1045 Год назад

      @@MilanJovanovicTech yes true it is! But for some people who are new to it they may want your experience with it. Yes we can add integration tests, unit tests , system tests etc but for a junior dev it’s a new concept.

  • @krccmsitp2884
    @krccmsitp2884 Год назад +1

    I like the approach and in our current project we actually apply it. With EF Core it is also flawless to be persisted.

  • @PrisonerZ3RO
    @PrisonerZ3RO Год назад +4

    I agree..."Strongly Typed Identities" make your method-parameters "more honest".
    And although it is cool...things like this often become overused and/or misused & I can see it quickly "blowing up" the amount of code for little (real) benefit.
    When implementing stuff like this, ask yourself...
    Q: Are we ACTUALLY EXPERIENCING the issue this approach solves? (probably not)
    Q: Does this approach increase or decrease complexity? (it increases it)
    Q: Does this approach increase or decrease the amount of conversion in-code? (it increases it)
    Q: Does it work seemlessly across every layer & technology we use? (nope)
    Q: Is there a simpler approach to make my method-parameters "more honest"? (clear parameter-names will do it well enough)
    Q: Can "clear naming" help ensure people pass the correct value? (yes)
    Q: Can "bad value" mistakes be caught in Unit Tests? (yes)
    Overall...
    Your code complexity increases & you will have to convert back-and-forth across MULTIPLE layers to solve a problem your not really having.
    As such...
    ATM, I would say while VERY COOL...the bang isn't worth the buck (as of yet)

  • @spontaneousorder5670
    @spontaneousorder5670 Год назад +1

    Great presentation. I'm just starting a new project and I'm going to use strongly typed IDs, for sure.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +2

      Good luck and be careful of overengineering

    • @DrGaurangGupta
      @DrGaurangGupta Год назад

      You seconded a thought which did cross my mind, and you did it quite descriptively.
      However, I do have a counter-argument. If the domain is not too complex, this might be an overkill. But for a large and complex domain with lots of entities, maybe this would be more helpful for clear understanding and maintainability.
      For example, the LineItems example shown in the video which has numerous Guid in constructor. There, this approach makes more sense especially when you need to simply change the order of arguments.

  • @JohnOliverAtHome
    @JohnOliverAtHome Год назад +4

    The one change (or addition) I would implement here is to add a static Create method to the Id types, thus hiding the internal implementation of the Id from the Customer object.
    ie. public static CustomerId Create() => new CustomerId(Guid.NewGuid());

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I agree that would be a good solution. But you would also end up writing more code. I guess it's an okay tradeoff to get an even cleaner design.

    • @andreasmewald2439
      @andreasmewald2439 Год назад

      What about casting operator ( Guid => ProductId) in the record itself?

    • @JohnOliverAtHome
      @JohnOliverAtHome Год назад

      @@andreasmewald2439 Using an implicit operator could work.
      public static implicit operator Guid(CustomerId value) => value.Value;

    • @andreasmewald2439
      @andreasmewald2439 Год назад +2

      @@JohnOliverAtHome it‘s less noisy and if you‘re using rider, rider will show you the implicit convertion in the inlay hints

    • @stefan-d.grigorescu
      @stefan-d.grigorescu Год назад

      Only from strongly typed ID to primitive type should be ok, but the reversed would bring back the problem of missplacing methods parameters, since primitives will be accepted due to implicit conversion

  • @medzik1993
    @medzik1993 Год назад +2

    There is a nugget package cold StronglyTypedId that allows implement this concept but it is based od structs.
    Records are under the hood a classes and they are allocated on the heap. And there is the question, which aproach is better and more effective ?

    • @krccmsitp2884
      @krccmsitp2884 Год назад

      It depends, as often. You can, however, choose between "record [class]" and "record struct" in C#.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I think it's cherry picking, but I'm not as keen on micro-optimizations.

  • @arthuraugsten
    @arthuraugsten Год назад +4

    I guess that would be good continue this video with another one showing about how to use this type as a original type with converters and serializers on controllers and EF Core. How to allow to use it as route parameters.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +3

      Yeah, that video is coming out next Friday. Trying to keep the videos bite-sized and only focus on one small topic. Next Friday's video will cover mapping all the DDD patterns with EF Core.

  • @z_prospective160
    @z_prospective160 Год назад +1

    I like how strongly typed id's can add so much more information rather than a property name and a primitive value. However, I am worried that this can cause much more complication when trying to generalize common code.. Like for example creating a base repository IRepository since the type of id is no longer abstractable.. You'd have to add another type parameter now I think.. like IRepository.. I don't know if I like that.. I have a bunch of boiler plate persistence libraries coded around an integer id.. and since I always name my Id's "Id" and I always know the type "int" then I can abstract those properties to an interface or base class. But I don't think you can really do that with strongly typed Ids can you?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      As you suspect, it introduces a lot of boilerplate if you want to generalize around it. Win some, lose some - right?

    • @jakecosilla
      @jakecosilla Год назад +1

      I think you will have to do like this
      public interface IRepository where TEntity : IEntity
      {
      TEntity GetById(TId id);
      void Add(TEntity entity);
      void Update(TEntity entity);
      void Remove(TEntity entity);
      }
      public interface IEntity
      {
      TId Id { get; }
      }

  • @siavash2176
    @siavash2176 Год назад +2

    Why not use readonly struct record? Why should I allocate one more extra object on the heap?

  • @andreasmewald2439
    @andreasmewald2439 Год назад +1

    Two questions:
    1. Wouldn‘t it be better to use record structs in your example? The strong typed id would behave like a value type in this case.
    2. If you implement implict cast operators in the record. Would EF be able to use the strong typed id ,when writing/reading to/from the DB?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      1. Maybe - are there other implications with using a `value` type in terms of copying?
      2. I do believe so, yes! But it's simpler to use a value conversion

  • @nayanc4353
    @nayanc4353 Год назад +1

    A record doesn't enforce immutability. It is syntactical sugar for class + ctor + init-property.
    You can have mutable properties in record.

    • @eugeneshevchenko4911
      @eugeneshevchenko4911 Год назад

      It'd better to use record struct for immutability.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +2

      If you try hard enough nothing is immutable :)
      A record define like in the video is immutable.

  • @z_prospective160
    @z_prospective160 Год назад +1

    The major advantage I can see to value objects is that let's say I have an identifier that is integer for customer id. If I decided that int is not big enough and decided to change the type to GUID then I can do that pretty much in one spot.. And everywhere that it is referenced gets that update. So maintainability and scalability is enhanced.. correct?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Yes - but what about the code creating that strongly typed ID? It'll also need to be updated

  • @Andy01010
    @Andy01010 Год назад +1

    I like the idea, enough benefits to use it - another advantage you could argue is that you can change the data type of the ID with minimal code description, or at least less stuff to refactor. Or the most practical use I can think of is that you can support multiple I’d types at the same time out of the box. In the past I defined them as value objects but record is waaay nicer. Value converters are pretty east in EF core but yeah it would be good if you show ppl few examples, might make them a bit more comfortable implementing ST ids

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Changing the data type will probably be a headache at the database level, so it's frail at best. I don't see why so much people hate this approach however.

    • @Andy01010
      @Andy01010 Год назад

      @@MilanJovanovicTech these days developing software requires a rather high degree of being open minded

  • @fxandrei
    @fxandrei Год назад +1

    Great stuff.
    Have you even thought about doing mini tutorials like this for frameworks like ABP or something similar ?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I don't think covering frameworks makes sense if I didn't use them in Production

    • @fxandrei
      @fxandrei Год назад +1

      @@MilanJovanovicTech Ok, got it. It would be quite interesting to see your take on some frameworks that are built with DDD in mind like ABP.

  • @RicardoValero95
    @RicardoValero95 Год назад +2

    Why would you place the id record on a different file? Why not before the class where you’re using it?

  • @sandrorevazishvili1113
    @sandrorevazishvili1113 Год назад +1

    Everything is explained perfectly but I still got some issues which indicating that I don't have primary key for PostId, I'm building blog where Post has strongly typed id PostId, which is record, any suggestions ?

  • @ryoman76
    @ryoman76 Год назад +1

    Great!
    one question can I type an integer (in the db it will become incremental) instead of a GUID?

  • @syedib
    @syedib Год назад +2

    If I have aggregate root and it gives id to my domain entity then how to implement strongly typed IDs . For example
    public class Product : AggregateRoot { }

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +2

      I'll try it out and see, but something like AggregateRoot should be possible

    • @whoost
      @whoost Год назад

      I have the same question....coulnd't get it to work with AggregateRoot

  • @justtomi-qp8qj
    @justtomi-qp8qj Год назад +1

    Insightful, thanks Milan!
    Although I learned something I wouldn't go with the strongly typed Ids.
    Why do you see ID as a complex type?
    Instead of primitive obsession you're getting class explosion plus all disadvantages can be easily solved and we wouldn't get the disadvantages that you mentioned.
    Disadvantages that you solved using strongly typed IDs:
    - In the LineItem example, when creating a line item and passing parameters, you might pass them in the wrong order:
    I see other problem of not using named parameters (or even in a combination with the positional ones) which can improve code readability and protect you from such problems.
    new LineItem(id: Guid.NewGuid(), orderId: Id, productId, product.Price)
    -In the Repository when getting Customer by Id
    Task GetByIdAsync(Guid id)
    Since it's a customer repository it by convention should accept customerId. If you accept productId your singature would (should) look different as well.
    Task GetByProductIdAsync(Guid id)
    Again this is my perspective,
    Thanks for sharing!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I appreciate all perspectives. I think it's a matter of finding a bug at design time vs runtime. Which do you prefer?

    • @justtomi-qp8qj
      @justtomi-qp8qj Год назад

      @@MilanJovanovicTech well in that question you left me no choice 😅

    • @vincentcifello4435
      @vincentcifello4435 Год назад

      @@MilanJovanovicTech Are you saying that your Unit Testing of the Domain would NOT find a bug caused by mistaken argument transposition?

  • @AshrafSada
    @AshrafSada 9 месяцев назад +1

    Thank you, I have one simple question: wouldn't be best to use a record struct instead of record, considering that primitive type GUID is a readonly struct?

  • @Luke_Ainsworth
    @Luke_Ainsworth 4 месяца назад

    Which layers would you expose your Typed ID to? For example would you make the command contracts in the Application layer use the typed ids? so the UI calling the commands would need to know about the typed ids? or would you just use a GUID and convert them in the command?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      Both can work, as the responsibility is the same. I'd probably do it in the handlers though

  • @unimatrix20a
    @unimatrix20a 11 месяцев назад +1

    I am wondering if this approach can stil be combined with inheritance from an abstract Entity class to get all the equality stuff back. E.g. a CustomGuid an then an Entity

  • @mcorven04
    @mcorven04 Год назад +2

    Thanks as always, Milan! How would you go on to implement the EF conversions for read and write?

    • @steve-wright-uk
      @steve-wright-uk Год назад

      Use a data transfer object DTO. When writing to the database, convert the domain model to a DTO and use that. Sinilarly when reading from the database, read into a DTO and then convert that to a domain model. It gives a cleaner separation between domain and the database. It also helps when you domain model is constructed from multiple tables on the database.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Coming out in Tuesday's video

  • @Ahmedhosnybarbary
    @Ahmedhosnybarbary Год назад +1

    Strongly typed IDs can make your code more expressive and improve the maintainability and correctness of your Domain-Driven Design (DDD) applications. In DDD, it's essential to model your domain entities accurately, and using strongly typed IDs is a technique that aligns well with this principle

  • @foonlam7134
    @foonlam7134 Год назад +1

    Hi Milan, one of the things you said about the strongly typed IDs is that they are immutable but in the case of the line item, what if you had saved a line item with a strongly typed product Id but later find that it was not the correct product and need to change it. Would that cause a problem if you need to change the product Id in the line item when it is supposed to be immutable?

    • @JohnOliverAtHome
      @JohnOliverAtHome Год назад +2

      Under this situation, you wouldn't change the Product in the Line Item, rather, you would remove the Line Item from the Order (or better yet set a flag on the Line Item) and add a corrected Line Item. This allows for audit tracking of the Order.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +2

      The ProductId itself is immutable - as an object. But on the LineItem, you can simply expose a method like ChangeProduct(ProductId) and replace the Product. Or simply create a new LineItem. Think about how you would fill your own shopping cart in the store?
      You remove something from your bag. You add something else.

  • @kiokokitheka
    @kiokokitheka Год назад +1

    Fantastic Content
    I am thinking of using a strongly typed id for a multi-tenant applicaiton which will result in many id's being composite for instance a CustomerId will consist of a guid for customer id and guid for tenant id. My question is, is this a good approach to solve such a problem secondly how would you map such a key on EF Core

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I'm not sure about the mapping. A value converter maybe. But I suggest you test the query support.

  • @matthewrossee
    @matthewrossee Год назад

    How do you approach referencing aggregate roots by other aggregate roots? As far as I know, we should avoid defining relationships between the aggregates, because we don't want changes in aggregate A to affect aggregate B. However, there's a problem in EF Core (Amichai recorded a video about it) that makes it difficult to create one-many relationship, because for example the ProductId in product aggregate should be a value object, but List in order aggregate should be entity type, to create table order_product_ids. Do you have some other way to do this? Because I don't like the Amichai's approach (although it's the best I've found)

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Why would an Order have a List however?
      Wouldn't it have Order -> List and then LineItem -> ProductId?

    • @matthewrossee
      @matthewrossee Год назад

      @@MilanJovanovicTech That was just an example where an aggregate has references to a list of other aggregate, I'm talking about the problem described here (ruclips.net/video/B3Iq346KwUQ/видео.html) by Amichai. Do you solve that differently, or just use ef core relations? Amichai said that we should avoid ef core relations between aggregates, so that's why I'm asking.

  • @greatpowerplay
    @greatpowerplay Год назад +1

    Strongly typing IDs (and other primitives that could be expressed as some sort of value objects) is a wonderful idea. However, problems or goofy decisions appears when time comes for those IDs to be serialized in one way or the other. How would you save those IDs in database? And, how would you deal with serialization if strongly typed id is part of DTO which is sent and received from front end?

    • @pedrofpsimoes
      @pedrofpsimoes Год назад +1

      That is also my concern. It will be somewhat cumbersome when working with a database or serialization. Imagine the JSON of it: "customerId": { id: "db7df04d-8d9d-4966-8949-f6638dc883eb" } and not only "customerId": "db7df04d-8d9d-4966-8949-f6638dc883eb"

    • @bartomiejoryszak6645
      @bartomiejoryszak6645 Год назад

      Highly recommend StronglyTypedIds nuget package. It uses Source Generators and gives you Json serializer out of the box. Also when working with EF it is easy to add a convention that saves the Id in the database the proper way

    • @dave7244
      @dave7244 Год назад +1

      It concerns me anyone would call this a "wonderful idea".

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +2

      Just serialize the raw value at the DB value. Not the object itself.

    • @krccmsitp2884
      @krccmsitp2884 Год назад +2

      You wouldn't use the strongly typed ID in DTOs, they exlusively live in the core layer, i.e. the domain. You also wouldn't reuse domain objects as DTOs. That said, you might need to do an object-mapping mechanism. Well known tools like, e.g. AutoMapper, support good ol' TypeConverters for such things.

  • @stjepanmajdak7396
    @stjepanmajdak7396 Год назад +2

    What about record struct?

  • @dcernach
    @dcernach Год назад +2

    That is great, but how can use these strongly types ids with Entity Framework?

  • @europeanleaguestaff
    @europeanleaguestaff Год назад +1

    When the primary key is composed of two primitive properties, creating a strongly-typed ID becomes problematic in the mapping with EFCore (even 8). Have you ever had to deal with this case?
    PS You're a great professional; since I started following you, I've started thinking differently.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      It's too problematic, probably want to avoid it with composite keys

  • @Mr43046721
    @Mr43046721 Год назад +1

    Its great idea for a global reactor of a big project)) have been wanting to implement this for a long time)

  • @michaldivismusic
    @michaldivismusic Год назад +1

    What about creating a CustomerId struct that simply inherits from Guid. Or is that impossible?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Isn't inheritance not supported with structs?

    • @michaldivismusic
      @michaldivismusic Год назад +1

      @@MilanJovanovicTech Oh, ok. The thought just came to me whole watching the video, nevermind then.

  • @tryagain6148
    @tryagain6148 Год назад +2

    And then the business comes and ask you why that feature you were supposed to deliver in 3SP just went up to 8SP. All of this only because we tend to overenginer for the wrong reasons

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      This won't add 5 SP don't worry

    • @tryagain6148
      @tryagain6148 Год назад +1

      ​@@MilanJovanovicTech I worked on a project which heavily used DDD. Now the project has over 3 mil lines of code. As this was some kind of modular monolith, we started to split it. The big bad wolf was that this project abused the DDD principles in the early days. The team worked mostly on features, over time we noticed (as a group) that we lost knowledge of some DDD practices from the early days, and even worse we started having issues with mapping POCOs because of situations like this one (that's what happens when you pick a "very" popular database engine). To add the cherry on top - everything was extremely configurable and every change could break the domain and business logic.
      So this was not the only cause but in the end, it was contributing to the huge increase in the expected work. So your 3 SP just got to 8 SP overnight...
      Even I agree with you on this one, if I would have a time machine and could rewind the time I would decide to keep the domain under KISS & YAGNY and add this kind of DDD practice under "I do not need it" - it's the app core and not a place where you dump anything you found on the internet or all the new business requirements.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@tryagain6148 But it does look like a lot more went wrong on this project - and the blame is probably on the engineers (which is typically the case) and not with DDD or any well accepted practice

  • @ДенисЕгоров-ь3в
    @ДенисЕгоров-ь3в Год назад +1

    There is an issue using strongly typed ids. Npgsql provider for EF Core doesn't support them as well as SQL provider.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Oh really? Well the next video is how to configure it with EF Core

    • @ДенисЕгоров-ь3в
      @ДенисЕгоров-ь3в Год назад

      @@MilanJovanovicTech try to configure it using HiLo. Also you can check issue #2617 in Npgsql provider for PostgreSQL repository

    • @piotr6254
      @piotr6254 Год назад

      @@MilanJovanovicTech I was goin to ask if you will do video about configuring it, but found the answer here :D

  • @dionismendanha4649
    @dionismendanha4649 Год назад +1

    I like use abstract class entity and create my id in this class with anothers comuns properties like createdAt, updatedAt. In this case i can use strong type for my ids?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Yeah, but you'll have to make Entity class generic

    • @adiviuh
      @adiviuh Год назад +1

      @@MilanJovanovicTech Hello. Could you recommend me the decision for case above? I've started to use ST Ids into my pet project. However I've met an issue with this approach.
      Imagine: There is an hierarchy of classes.
      Driver - RideParticipant - User - AggregateRoot
      And
      Passenger - RideParticipant - User - AggregateRoot.
      I've used Guid before, but now it would be great if I'll have ids DriverId and PassengerId. All other are abstract classes and in many businesses cases an inheritance could be very huge (for a well designed too).
      So, there is a problem. If I want to have it, I need to make abstract classes as generic. However I want to use them as non-generic types, because there are classes that applies that abstractions and I don't want to make everything generic recursively.
      What can you suggest?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@adiviuh Try to refactor into composition instead of inheritance

    • @adiviuh
      @adiviuh Год назад

      @@MilanJovanovicTech I'm not sure, that it is the best option here) Thanks for answer.

  • @adamlazar2355
    @adamlazar2355 11 месяцев назад

    Does this remove the need for an id property a base entity class then? Because each entity will have a specific id type.
    I've been going over your pragmatic clean architecture course, and in there, you have a base class for entities that takes a guid id for the constructor. But if that base property gets removed, i lose the safety of making sure each entity has an id defined.
    I tried making the entity bass generic with a TId, but it seems to be getting out of hand fast.

    • @MilanJovanovicTech
      @MilanJovanovicTech  11 месяцев назад +1

      I had that in the course in V1. But I removed it, because it adds too much complexity for not much value.

    • @adamlazar2355
      @adamlazar2355 11 месяцев назад

      @MilanJovanovicTech makes sense. Thanks for the new content, also!

  • @ezecel9
    @ezecel9 Год назад +1

    Do you have in mind to make a video about integration testing using a docker container? Thanks

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

    Awesome and Useful content

  • @MahmoudSaed98
    @MahmoudSaed98 5 месяцев назад

    Thank You Millan .
    I have a question
    If I have a Person entity and a Customer entity and the Customer entity inherits the Person entity
    How do I use Strongly Typed ID
    In this case

    • @MahmoudSaed98
      @MahmoudSaed98 5 месяцев назад

      For example, if you create a strongly typed id for the person entity and another for the customer entity, but since the customer entity inherits the person entity, it certainly gets the person entity id. How do we solve this problem?

    • @MilanJovanovicTech
      @MilanJovanovicTech  5 месяцев назад

      Use the PersionId?

  • @KashmirThakur-goldi
    @KashmirThakur-goldi Год назад +1

    How we can use strongly typed ids with database generated values

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I do believe if you configure it with EF Core it'll work just fine, but I didn't check with DB generated IDs

  • @harryh212
    @harryh212 Год назад +1

    Would it be an idea to call new guid by default inside the default constructor of a strongly typed id so that new guid isn't called outside the new record. It Could still make muddle up if you write new LineItem(new LineItemId(product.Id.Value).....), even though that seems ridiculous 😅😅

  • @DarlissonLimeira
    @DarlissonLimeira Год назад +1

    Hey, do you have some books for reference about ddd?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      - Domain-Driven Design, Eric Evans
      - Learning Domain-Driven Design, Vlad Khononov

  • @crikey4336
    @crikey4336 Год назад +1

    On one hand I am a big fan of strongly typed entity identifiers, but i really wish this demo used record structs instead of just records. In the domain model, identifiers are almost always conceptualized with value-type semantics and not reference-type semantics. I think turning a primitive type identifier into a domain-specific *reference* type identifier throws the baby (value-type semantics) out with the bathwater and ends up turning lists of identifiers from something fast, lightweight and space efficient in the primitive obsessed world, into something that ends up being heap allocated for every identifier. I think part of the reason for primitive obsession is the simplicity and performance. With record structs you get it all - a clean type-safe domain model AND all the safe performance and space-savings of value types.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      What did you use before we had records?

    • @crikey4336
      @crikey4336 Год назад

      @@MilanJovanovicTech I use a readonly struct type to model these domain ids and implement overrides for equals, operators, hashcode and casting operators to streamline conversion back and forth to primitive types for DTO's to the database

  • @blackmagic9921
    @blackmagic9921 7 месяцев назад

    Hello Milan, why would you choose guid over int pk in ddd

    • @MilanJovanovicTech
      @MilanJovanovicTech  7 месяцев назад

      Based on what are we making the decision? Is DB performance important?

  • @winstochurgle9133
    @winstochurgle9133 9 месяцев назад

    Yo, Milan. Did you ever get an exception while creating dbContext called "Unable to create a 'DbContext' of type ' '.The entity type 'ExampleEntityId' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'"?
    Even though I have specified this entity as the primary key of my class using fluent api. Can't find a solution.

    • @MilanJovanovicTech
      @MilanJovanovicTech  9 месяцев назад

      Did you also configure a converter for the ID?

    • @winstochurgle9133
      @winstochurgle9133 9 месяцев назад

      @@MilanJovanovicTech
      Yes i did:
      builder.Property(p => p.Id).HasConversion(
      categoryId => categoryId.Value,
      value => new CategoryId(value));
      that's why i find this exception weird.

    • @winstochurgle9133
      @winstochurgle9133 9 месяцев назад

      @@MilanJovanovicTech I fixed the exception. It consisted in the fact that I incorrectly configured the foreign key for an entity in another class.
      How was it:
      builder.HasOne(x => x.CategoryId)
      .With Many()
      .HasForeignKey(x => x.Id);
      How to do it correctly:
      builder.HasOne()
      .With Many()
      .HasForeignKey(x => x.CategoryId);

  • @swozzares
    @swozzares Год назад +1

    This doesn't always work, for instance with composite keys.
    Tables Tenant(TenantId) and TableA(TenantId, AId).
    Now TableB(TenantId, BId) wants to link to TableA with extra field (AId) so TableB(TenantId, BId, AId). The TenantId is now shared; if you don't share the key you risk links to data belonging to another tenant! If you use a strongly typed id then the TenantId gets duplicated, is redundant and can still point to another tenant.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      That makes no sense. If you pass the same TenantId to both TableA and TableB how can it be the wrong tenant?

    • @swozzares
      @swozzares Год назад

      @@MilanJovanovicTech If you use strongly typed ids, then TableB would be (TenantId, BId, TenantId2, AId) - first two fields are the key for TableB, second two are the key reference to TableA; now there is nothing stopping you from linking an entry in TableB to an entry in TableA that belongs to a different tenant.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@swozzares So why not have (TenantId, BId, AId) - where the TenantId is shared?
      Same can be done in the code

    • @swozzares
      @swozzares Год назад

      @@MilanJovanovicTech Thats what I said in the first place! lol, I don't think you are understanding the point.

  • @deefstes
    @deefstes Год назад

    Another very useful DDD concept that I've learned from @MilanJovanovicTech is the use of value objects. But I'm a little unclear as to the difference between the two. Would a value object not have been just as suitable, or better, to use for the ID? Would anyone care to explain the difference?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Records are immutable, so you can consider this a value object. Don't think about a value object as "it inherits from a class". Think about it from the qualities it has: immutable, represented by its value.

  • @vagnerwentz1189
    @vagnerwentz1189 Год назад +2

    But I would like to know, what's the real benefit about it?

    • @Kingside88
      @Kingside88 Год назад

      I also don't get the point. Dealing with Api and Database Logic will getting be pain in the you know what

    • @steve-wright-uk
      @steve-wright-uk Год назад +2

      @@Kingside88 The real benefit is that you can't accidently transpose parameters when calling methods.
      Database logic isn't a problem for us as we never pass the domain object directly to EF Core. We always create a data transfer object (DTO) from the domain object and use that. Similarly, when reading from the database, we reading into a DTO and then create the domain object from the DTO.

    • @jpsytaccount
      @jpsytaccount Год назад +1

      The only benefit I see is not being able to the use the identity where you’re not supposed to. Example, using a UserId to get an Office. Obviously doing this would result in a bug, but with this solution, it would cause a design-time error.

    • @steve-wright-uk
      @steve-wright-uk Год назад +4

      @@jpsytaccount Yes - and that's a massive benefit. The quicker you pick up these mistakes the cheaper it is to fix it. You'd like to think that unit testing and QA will pick up these mistakes, but in the real world, that doesn't always happen. Even if they do, the cheapest bug to fix is still the one that the compiler detects..

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Others have chipped in with some pretty good benefits. I like it because it makes everything more expressive with strong typing.
      Of course if you feel this is overkill, no need to use it. I don't like to be dogmatic about any design pattern, and leave it up to the developer to pick and choose what to use.

  • @batuhanaydn4592
    @batuhanaydn4592 Год назад +1

    Maybe using readonly record struct is a better idea? A class seems like an unnecessary indirection here.

  • @EldonElledge
    @EldonElledge Год назад +1

    Great video as usual. But, when using Records this way, you are taking a performance and memory hit.
    Based on your use, it may be better to use a struct with IEquatable as shown in the example below.
    public struct CustomerId : IEquatable
    {
    public CustomerId(Guid value) => Value = value;
    public Guid Value { get; }
    public bool Equals(CustomerId other) => Value.Equals(other.Value);
    public override bool Equals(object? obj) => obj is CustomerId other && Equals(other);
    public override int GetHashCode() => Value.GetHashCode();
    public static bool operator ==(CustomerId left, CustomerId right) => left.Equals(right);
    public static bool operator !=(CustomerId left, CustomerId right) => !left.Equals(right);
    public override string ToString() => Value.ToString();
    }

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Isn't that too verbose? Especially with many strongly typed IDs. I have to explore record structs, though

    • @EldonElledge
      @EldonElledge Год назад

      @@MilanJovanovicTech, it is a little verbose. It would be a mater of deciding if being a little verbose with worth not taking the performance and memory hits for using Records. I believe Records use reflection under the hood and that could be why there is that hit.

    • @DrGaurangGupta
      @DrGaurangGupta Год назад +1

      Benchmark-ing might help make the decision between the four: class, struct, record and record struct.
      This has got me intrigued.

  • @Luke_Ainsworth
    @Luke_Ainsworth Год назад

    Hey Mate, great video :) Could you make one on how to define a composite key for a join table? I.e. Key is two foreign keys to entities a & b. I've got a requirement for a join table between two entities that also has extra info associated with it. Not entirely sure how to implement that with DDD

  • @vamvdotnet
    @vamvdotnet Год назад +1

    Hey Milan! Where's your new MVP trophy? Add it to the background :) ! 'Til next time!

  • @luboshemala3485
    @luboshemala3485 Год назад +1

    Why not struct?

  •  Год назад +1

    Usage of strongly typed ids for Guid, int or long just misunderstood. If you have strongly typed ids you should define custom business identifier instead of database id. Let's say you have TrackingId for your shipment, that TrackingId should be generated in domain layer with logic.

  • @zameer.vighio
    @zameer.vighio Год назад

    Good work Milan!,
    my Question is out of this topic. Note:- My english isn't good enough 😅 i hope you will get the point?
    we use Automapper to map mostly DTO & DB models, which saves lines of code for assigning.
    Question is if we use mapper inside .Select(s => _mapper(s)) during fetching data using Efcore then it maps on client site but i want to map data on server side,
    how to handle automapper to map data on server side.

    • @zameer.vighio
      @zameer.vighio Год назад

      and i'm still waiting for SHARED Culture video in net core. as few days earlier you said you will do it.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      `SHARED Culture` remind me about that one again? 🤔
      I think you should take a look at AutoMapper Projections to map on the DB side

    • @zameer.vighio
      @zameer.vighio Год назад

      ​@@MilanJovanovicTechif you can provide just a short, that will be helpful.
      Thanks

    • @zameer.vighio
      @zameer.vighio Год назад

      @@MilanJovanovicTech 'Shared Culture' Globalization & localization

    • @z_prospective160
      @z_prospective160 Год назад

      yes automapper projections.. I thinkt the automapper library has an IQueryable extension method called ProjectTo()

  • @murat.yuceer
    @murat.yuceer Год назад +1

    What is benefit

  • @amirmirzababapour6016
    @amirmirzababapour6016 5 месяцев назад

    Hello Milan, I was looking for your email but it seems you already answer questions in the comments and I appreciate that. Recently I have a question and the first person that came to my mind to ask it was you. In a book I was reading, it suggested to ask someone who has gone the path you want to go, the question of how they think about their experiences and whether the effort was worth it to get there. As a software engineer, how would you go about this question? I respect what you’re doing, but I imagine it has
    high points and low points. Could you share them with me? Knowing what
    you know now, is it worth the effort?

    • @MilanJovanovicTech
      @MilanJovanovicTech  5 месяцев назад

      Anything worth achieving is always worth the effort :)
      Sounds a bit cliche, but it's true. Was it hard getting to where I'm at? Heck yeah. But I don't regret it. The highs are awesome, and the lows can be painful. What I try is to keep moving "forward" each day, at least a little. Wherever "forward" is (you have to decide for yourself). Not sure if this answers your question.

    • @amirmirzababapour6016
      @amirmirzababapour6016 5 месяцев назад

      @@MilanJovanovicTech If I'm not not troubling you with my questions, If you were to go back, would you go into a different field? why?

  • @alirezayari-b9b
    @alirezayari-b9b Год назад

    thanks for the the video

  • @disorientatedgamer3141
    @disorientatedgamer3141 Год назад +1

    Oh boi, this is really confusing I though in your recent video you said the entities could be within Infrastructure but now you are using in the Domain project; haaah haha. My name is also true for coding, it seems.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I doubt I said entities go in Infra - which video?

    • @disorientatedgamer3141
      @disorientatedgamer3141 Год назад

      ​@@MilanJovanovicTech You are right, sorry I was thinking about the repository interface; it's a tiny bit complicated because there are a lot of data to take in. In mean timetime watched more of your video and there seems to be multiple ways, so it's not strange anymore. 😅

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

    We can use struct record instead of class record. I believe.

  • @tjmukurumbira
    @tjmukurumbira Год назад

    Primitive types with Named Parameters..Wont the same problems occur if you have more than one Parameter with the same strongly type id.

  • @pedroferreira9234
    @pedroferreira9234 Год назад +1

    its cool, but instead of passing OrderId for example we can pass the Order itself

  • @pdevito
    @pdevito Год назад +2

    I think I prefer nick’s example: ruclips.net/video/z4SB5BkQX7M/видео.html

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      He's using the library I recommended at the end of the video. It's a bit out of date at the moment, but still an okay solution

    • @pdevito
      @pdevito Год назад

      Ah, I hopped off at like 90% so I missed it! Regardless, thanks for sharing. Always enjoy seeing how others like to work

    • @TheZeppelinShark
      @TheZeppelinShark Год назад

      Thanks for the link. Nick's video does give some much needed context with dto/API and domain and doesn't wrap a struct in a class. Still think this is borderline over engineering, but seeing the API to domain conversion and serialisation makes me think there is a good argument for these.

  • @dave7244
    @dave7244 Год назад +1

    This is what is known as over-engineering and there is more code with barely any real world benefit.
    In your last example with the repository get by ID method you already know that you need the ID itself because the method is called "GetByID" so the code will typically read "myThingRepo.GetByID" and it should be returning a singular of . If someone can't work that out then they shouldn't be using a text editor.
    Also I've seen primitive obsession where you have long list parameters where everything is a string or int. A struct / class is the obvious replacement. What you are presenting here isn't really primitive obsession. Is really passing a Guid to a clearly labelled method really primitive obsession?
    The only example I think where it might be beneficial is in a constructor example with many IDs. Even then it like marginal benefit. You could just use a class with clearly labelled IDs.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      I agree it leans towards over-engineering, but that's if you aren't too keen on DDD in the first plsce

    • @dave7244
      @dave7244 Год назад

      @@MilanJovanovicTech I don't think DDD requires over-engineering.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@dave7244 There are many flavors of DDD

    • @dave7244
      @dave7244 Год назад

      @@MilanJovanovicTech Doesn't really answer the criticism.
      I can achieve exactly the same thing is a regular Guid Id field.

    • @TheZeppelinShark
      @TheZeppelinShark Год назад +2

      I agree, this seems to just add complexity where there is no need. The agrument about the method signature being strongly typed seems a little silly, well what if I can that code an create a new productId instead of passing the productId, this feels just as stupid as not using the correct ordering (the argument names do serve a purpose).
      I feel like videos like these mislead beginners into over engineering.

  • @danflemming3553
    @danflemming3553 5 месяцев назад

    I'm not convinced for the need of strongly type Ids. They look cool, but they have very little to none practical usefulness. There's not much primitive obsession in this case because there's not much to encapsulate in my opinion.

    • @MilanJovanovicTech
      @MilanJovanovicTech  5 месяцев назад

      There's no need for it, sure. What I did wrong in this video is not using a better example for the ID. Instead of just wrapping a primitive type, I should've shown a "meaningful ID". I'll make a post about it. In general, strongly typed IDs _can_ bring some qualities that you may find useful, so that's when you should consider using it.