I'm actually one of the people who opened a github issue for this, and let me just say after weeks of exhaustive solution implementations I'm really glad you are back. 👏❤
Yes I agree is nice to have our ids properties strongly tipped in the other hand sometimes I feel this is too over engineering which is the opposite of clean code.
I usually keep the inner identifier private and implement implicit and explicit conversions to string or whichever inner type we are using. This way, all of the domain code doesn't know, but any dependencies (like EF or something like a httpclient can use the string as needed)
My thought process it that the problem is with EF, which is a tool for our infrastructure layer and therefore our domain should not be responsible for the solution of the problem but rather the infrastructure itself. Therefore for me, the most suitable solution of the ones you mentioned is the first one. This I think also makes it easier if in the future EF decide to support this to change the infrastructure layer.
Wow! I started this amazing serie about CA and DDD and I can't stop. Congrats for your job. No words to describe the effort and the passion put in this. Again thousand thanks @Amichai 👌🤓.
Many thanks. I found myself Laughing Out Loud when you reached the not recommended solutions because I tried the first three in my pet project where I'm trying all you taught us in these videos, and discarded the first two because of the SAME reasons.
IMHO, the main problem is an existence of Id properties in the domain objects. If we've got rid of foreign keys in dependent objects using shadow properties, for me it's just absolutely necessary to make Id properties shadow as well. No Id - no problem. Ids are DB realm concept, not object one. What do you think?
Welcome back - I've never modeled a domain to this extent using EF Core so not hit the problem you were facing but I like the solution, seems nice and clean, no real downside?
It's not a common bug to put the wrong id in the wrong column, and you can even enforce to write them as (userId: userId, tenantId: tenantId) to read it better. And the penalties you get is a lot worse than what you get (that's not needed). You have less readability, more code to write everywhere, it's slower and use more cpu. All this for a non problem.
I share your opinion most of the time. I’m covering the religious DDD approach for educational purposes. However, there are cases where strongly typed IDs can be helpful, and I don’t think we should disregard them as a whole. I deal with versioned string-typed IDs that are a combination of 4 or 5 other IDs regularly within Microsoft. This is a prime example of where strongly typed IDs, regardless of DDD would make several code bases less error-prone
Amazing to see you again. A question. why do you prefer StronglyId has a "class" type Wouldn't "record struct" or "struct" be preferable? To lower the GC pressure?
Generally yes. The architecture I’m demonstrating in this series isn’t very GC friendly, especially with all the various objects and MediatR, so memory/runtime sensitive applications should probably model their system differently. But to your question, I haven’t given struct enough thought here to say if non-destructive mutation or stack allocations can present issues. I’ll have to play with it and come back to you 🙂
My strongly typed Id are all structs and I have generic converters for json serialization and EF Core. In the database they all became strings, like the Guids, DateTimes (in most dbs), etc. There should be no reason for you to want to keep more than that in the database, if it is simply an Id and a value object. The problem you mentioned is related to complex objects, EF Core supports working with them and, if I am not mistaken, in future versions, they will support save them as json in the database, like document dbs. If you can give me more information, I can try to understand why this is such a big deal for you to make a whole video mentioning it as an "unsolvable" paradox. All the best.
Welcome back and awesome video! I love this series! I think the simplicity of the "creative hack" is worth the minor costs of developer dogmatism :) Your solution requires much less work and maintenance than "the right way" AND you're having to do this because of the limitations of known issues that pretty much have "Won't Fix" resolutions (at least not anytime soon). Fantastic job!
One of the most genius rules about programming, that I really believe it: YOU ARE'NT GONNA NEED IT. So, apply abstractions when they really make a difference, not in the hope of some day that they will become useful. Also, the reasons that you mentioned to support this pattern are true about ALL fields, not just ids, so with this approach, maybe we will have one value object per field. I refer to Eric Evan's opinion in his book, when he talks about standalone objects, that making dependencies adds complexity to the code and demands more effort to understand it, so when you can express a field by a primitive type, you have the chance to eliminate one dependency, and you should definitely do it, especially when you have no logic to be encapsulated with that value.
Your domain-layer objects should not be your EF models. Breaking single responsibility principle. 8:00, 8:45 Mapping is overhead but that's the trade-off for robustness and having an anti-corruption layer. 9:15 Creating a mapping cannot introduce bugs compared to switching IDs in the function arguments which definitely will and these will be very hidden bugs.
Hi, great series. In DDD we use GUID for Id type because entities ids must be unique across our domain. But in database using GUID as primary key (with clustered index) has performance issue specially in heavy insert scenarios because of randomness of GUID Ids. What can we do about it?
There are solutions for index-friendly GUIDs such auto-incrementing GUIDs. The ABP framework e.g. implements a GUID generator that generates pseudo GUIDs for that purpose.
Yeah, you can change a GUID to a string, but if you change a string id to a Guid, then you might break existing string values that don't meet the Guid requirements (unless I'm misunderstanding what you're recommending).
Strongly typed Id is really great, but the problem you faced I found out is in the EFCore migration generation process. If you mention configuration instances that are not having the "OwnedMany()" methods first and then mention the configuration instances that have "OwnedMany()" methods, then "EFCore" is able to generate the perfect migration files. Maybe this behavior is happening because of using reflection heavily. However, I'm not sure about this. Maybe you and other experts could find out that and can issue a bug to the Github repo. 😊
Was the correct part of the migration shown at the end? The example is about Host having many MenuIds. But we see a table of MenuDinnerIds, i.e. combining Menu and Dinner? And it looks like you would only get one foreign key constraint, back to the "owner", i.e. to Host (or, in the shown migration to, Menu). But there is no FK constraint on the MenuId, so you can have invalid references in the database? Generally I would expect the end result to be a join table in the database, with references to both Host and Menu. Your result ends up with only one foreign key.
Welcome back! Where were you? Missing your videos. Now, I see an upgrade in your drawings. What is the tool now? It seems it is not longer ZoomIt! Good job again!
Currently implementing this is heavy (a lot of code and relatively complex code) that it does not justify the benefits (you also need to do work on the MVC side to make it map the ids). It would be cool if C# had some kind of type aliases where you can just give names to existing types which would make it simpler to use and support in libraries like EF and MVC as they would just need to recognize the actual type and treat the value as such while the compiler takes care of wrong usage
The part that I don't understand is that you said the ID is a VO, yet a VO should not have any identity, this is the key difference between an entity and VO.
I thought the object-type configured with OwnsMany should be recognized as a Non-Entity Type? Can someone explain cuz he said in the video that MenuId in HostAggregate config is an Entity Type
Hi Amichai! I have a question, For queries with multiple related entities like getting the Menu with the list of Dinners and MenuReview which sits on different Aggregate Root. What do you think you'll use on that? I'm so happy to subscribe to your Patreon. You're such a blessing in the community. :)
The best thing you can do concerning EF, is just use something that benefits your life and organization. EF doesn't provide any additional value, only introduces new problems. From knowing the intricacies about how the change tracker actually works, to knowing what linq is valid, what the valid linq translates to, to how easily it is for devteams to mangle the snapshot, to its obscured concurrency capabilities (that only the most knowledgeable devs know how to do), it is just never worth it. See how I didn't even mention sql performance? I don't think sql performance along is enough to choose an ORM. It really boils down to the enormous amount of learning that needs to occur in order to maintain EF applications. I've been working with it for over half of my career, and the development shops that wisely choose not to use EF are rewarded handsomely. Just use something like dapper and manage migrations through fluentmigrator or something similar. The non-EF shops ALWAYS have a much more pleasant time with data access and database maintenance. EF adds nothing but complexity and headaches
I have 2 unrelated sets of tables (Menus and Hosts) after adding the Host aggregate code and adding its tables to the database. Is this how it should be? Isn't the HostMenuIds table a many to many relationship table for two aggregates Menus and Hosts? Should these two aggregates be linked, or was it originally intended to make an unlinked set of aggregate tables for further division into microservices with separate bases?
In the BuberDinner's codebase as far as I know you're supposed to raise a new domain event like "record MenuCreatedDomainEvent(MenuIn menuId, HostId hostId) : IDomainEvent". When saving changes to the database, the saving changes interceptors should publish this event and the MenuCreatedDomainEventHandler : INotificationHandler should take care of linking the MenuId to the Host. So it could look like this: var host = _hostRepository.GetById(notification.hostId), then check if host is not null, and eventually perform the linking operation: host.AssignMenuId(menuId). Remember not to call SaveChangesAsync, you don't need any unit of work in the event handler, because when all event handlers have finished their work the dbcontext is gonna save changes. I guess for a distributed system you could imagine a situation when domain event handlers publish some sort of integration event to a message queue like RabbitMq, so the other microservices can update their db state. I hope it helps!
Entity framework migrations are only good for simple project. Pretty useless in a big company which needs the DB schema to be the same in all environments. Will MenuId:AggregateRoot not work if there in a implicit cast to string? For JSON serializer we need to give a conversion function. It was in my todo list that your video now reminded me to do.
Generics usually make the code more complex and harder to understand. I don't think most applications need this kind of overhead. I use Presentify for the arrows and rectangles 🙂
I am having issues migrating like this. It keeps telling me that (for example) no suitable c-tor was found for 'HostId', so I go ahead a make a paramterless one there (which is something you didn't have to do), then the error changes to the entity type 'HostId' requires a primary key.. etc. I essentially keep going in a loop, adding `HasNoKey()` to X Id Value Object, then it says that I cannot be Keyless, and suggests making the AggregateRootId keyless, which initself brings another error, and I just cannot get it to work for whatever reason, and my project is basically 1:1 with yours. Been debugging for a few hours now, reading stuff online, but nothing seems to be working. Would be great if anybody has suggestions.
Watch closely in the video. He’s not going from the last version to this version but rather deleted the old one and is executing “add initialCreate” again
@@amantinband what do you mean by OwnsMany/OwnsOne defines the type as an entity type.. what do u mean by entity type ? this video made me so confused :(
Hi and thank your for your great afford. I have a question! What about the MenuId column, in the HostMenuIds table? Shouldn't it be a foreign key to the Id column of the Menues table? Currently following you toturial, I'm missing this relation! And I think it's important to have it, in case of deleting menu, we can cascade it to delete the corresponding record in the HostMenuIds table Thank you very very much👌
Well, I think I found the answer in the next video, Domain Events! As you mentioned there, aggregates are transaction boundary, And we dont want changing an aggregate to have side effect on others, and that's why we don't setup foreign keys! But this brings me to next question, so why do we try to set foriegn key for the host-menu relation as described in this video?! Deleting a host, would delete the menu... isn't a side effect?!
@@VahidRassouli This is very impractical. DDD doesn't need to be followed to the 100%. If you don't add FK, you are risking data integrity and there is no point of using relational database then. If you do not use cascade, there is no possibility of side effect.
A question here, how would you implement a many to many relationship with this approach ? for example if you have an A-aggregate who owns list and this B-Aggregate is suppose to own List this would throw the same exception as before, how can we solve this?
@@amantinband Yes I'm referencing a List of Ids in both and efcore throws the same type of exception when trying to add a migration. Aggregate-A has List Aggregate-B has List
parameter objects for the function and unit testing. problem solved. Why coming up with some new creative ideas to clutter a project with yet another approach for already solved problems?
I've practiced domain driven with ef core for two years now without ever having to map up identities as entities. I don't understand why you would wanna do this? To me, it feels like this ain't a problem with the feature set of ef core, but some other misunderstanding on how you should model aggregates.
The IDs are value objects, not entities. If you’re interested in learning more, you can check this out: www.informit.com/articles/article.aspx?p=2020371&seqNum=4
Why make the Id property of the AggregateRootId abstract? Why not just implement it in the base class itself then you wouldn't need to override it in every inherited class.
This is pointless because if both ValueTypes take a string anyway, the mistake can still be made where a new UserId is constructed with the tenantId, and vice versa, so you just moving the issue up the pipeline, this does not solve anything
Your problem lays in the architecture. Do not use EF in your business logic and you will not face such kind of problems. (if problem does not exist, a solution for it is not needed)
The codebase keeps changing between videos. If you're following along you'll struggle unless and even with being a patreon member and having access to the source code.
We've NEVER had problems identifying the IDs in our projects i feel this is another over engineering that creates more problems than it solves. The more i learn about "advanced" software development the more i learn about problems that no one ever had. Why stop there, lets encapsulate every single property, then copy it at least 5 times. This solves a minor problem at best. Do these techniques just get pushed so someone can sell more books?
This is not overengineering. EF Core 8 now officially supports this feature. If this is over-engineering, microsoft will not provide the feature in the first place.
2 місяці тому
I agree that this is not an usual problem to have in small teams, maybe the problem of confusing arguments could happen in a project where hundreds of people work on the same code base and they don't know each other or it's a community driven open source project. In any case, you will have other mechanisms to detect this error: unit tests, integration tests, manual tests, etc tests... code reviews, peer reviews, and some tools might give you hints that the names do not match... so I would only use the technique of having value objects for Ids when it's absolutely necessary. In other languages like Typescript this would make more sense....
For some reason I get this exception when I use the register endpoint. Mapster.CompileException: Error while compiling source=BuberDinner.Application.Authentication.Common.AuthenticationResult destination=BuberDinner.Contracts.Authentication.AuthenticationResponse type=Map ---> System.Reflection.AmbiguousMatchException: Ambiguous match found. ...
In the mapping configs, you can enforce .ToString() on the src.MenuId.Id.Value. This makes it work as it can parse the string value to a Guid just fine.
How about 1-1 relationship : Ex: 1PurchaseOrder can belong to 1 Vendor public class PurchaseOrder : AggregateRoot { public Vendor Vendor { get; private set; } public VendorId VendorId { get; private set; } } // Vendor builder.Property(p => p.VendorId) .HasConversion( id => id.Value, value => VendorId.Create(value)); // TODO builder.HasOne(p => p.Vendor) .WithMany() .HasForeignKey(p => p.VendorId) .IsRequired(); But I can not do this
I'm actually one of the people who opened a github issue for this, and let me just say after weeks of exhaustive solution implementations I'm really glad you are back. 👏❤
Now this problem is fixed on EF core 8, but this is a very helpful tutorial, i can't find any course that teach about DDD better than your course
Do you by chance have a link to where I can see this solution? I can't find the fix.
Do you have any link to it ?
Yep, this is definitely fixed in EF Core 8, just tried it.
Good to see you back 🔥
why you don't do something together 😄 you both provide the domain driven design and design patterns in the best way
@@nawarali1912 I agree. Milan + Amichai = CA + DDD
Yes I agree is nice to have our ids properties strongly tipped in the other hand sometimes I feel this is too over engineering which is the opposite of clean code.
I tend to agree. I think there very specific applications need to follow these practices to the T
I usually keep the inner identifier private and implement implicit and explicit conversions to string or whichever inner type we are using. This way, all of the domain code doesn't know, but any dependencies (like EF or something like a httpclient can use the string as needed)
I like that approach as well
Do you have any repo on GitHub where I could look at your solution? It seems interesting.
Ahhhhh yeah, he's back! So happy we get more top tier content.
My thought process it that the problem is with EF, which is a tool for our infrastructure layer and therefore our domain should not be responsible for the solution of the problem but rather the infrastructure itself. Therefore for me, the most suitable solution of the ones you mentioned is the first one. This I think also makes it easier if in the future EF decide to support this to change the infrastructure layer.
Wow! I started this amazing serie about CA and DDD and I can't stop. Congrats for your job. No words to describe the effort and the passion put in this. Again thousand thanks @Amichai 👌🤓.
Many thanks. I found myself Laughing Out Loud when you reached the not recommended solutions because I tried the first three in my pet project where I'm trying all you taught us in these videos, and discarded the first two because of the SAME reasons.
Good to see you back 🎉
Long time pal.
IMHO, the main problem is an existence of Id properties in the domain objects. If we've got rid of foreign keys in dependent objects using shadow properties, for me it's just absolutely necessary to make Id properties shadow as well. No Id - no problem. Ids are DB realm concept, not object one. What do you think?
Have you tried this in a project before? Do you have an example you can send me?
Back with a bang!
I struggled with this and ended up succumbing to your 2nd bad solution. Look forward to going back to implement this solution!
Welcome back - I've never modeled a domain to this extent using EF Core so not hit the problem you were facing but I like the solution, seems nice and clean, no real downside?
It has some overhead, complexity and requires breaking “persistence ignorance”, but out of the solutions, this is definitely better IMO
It's not a common bug to put the wrong id in the wrong column, and you can even enforce to write them as (userId: userId, tenantId: tenantId) to read it better. And the penalties you get is a lot worse than what you get (that's not needed). You have less readability, more code to write everywhere, it's slower and use more cpu. All this for a non problem.
I share your opinion most of the time. I’m covering the religious DDD approach for educational purposes.
However, there are cases where strongly typed IDs can be helpful, and I don’t think we should disregard them as a whole. I deal with versioned string-typed IDs that are a combination of 4 or 5 other IDs regularly within Microsoft. This is a prime example of where strongly typed IDs, regardless of DDD would make several code bases less error-prone
Nice one. Welcome back!
Amazing to see you again. A question. why do you prefer StronglyId has a "class" type Wouldn't "record struct" or "struct" be preferable? To lower the GC pressure?
Generally yes. The architecture I’m demonstrating in this series isn’t very GC friendly, especially with all the various objects and MediatR, so memory/runtime sensitive applications should probably model their system differently.
But to your question, I haven’t given struct enough thought here to say if non-destructive mutation or stack allocations can present issues. I’ll have to play with it and come back to you 🙂
Great!! You are back!!
My strongly typed Id are all structs and I have generic converters for json serialization and EF Core. In the database they all became strings, like the Guids, DateTimes (in most dbs), etc. There should be no reason for you to want to keep more than that in the database, if it is simply an Id and a value object. The problem you mentioned is related to complex objects, EF Core supports working with them and, if I am not mistaken, in future versions, they will support save them as json in the database, like document dbs. If you can give me more information, I can try to understand why this is such a big deal for you to make a whole video mentioning it as an "unsolvable" paradox. All the best.
Amazing content. Thank you!
Welcome back and awesome video! I love this series! I think the simplicity of the "creative hack" is worth the minor costs of developer dogmatism :) Your solution requires much less work and maintenance than "the right way" AND you're having to do this because of the limitations of known issues that pretty much have "Won't Fix" resolutions (at least not anytime soon). Fantastic job!
Thanks, Tim! 🙏
One of the most genius rules about programming, that I really believe it: YOU ARE'NT GONNA NEED IT.
So, apply abstractions when they really make a difference, not in the hope of some day that they will become useful.
Also, the reasons that you mentioned to support this pattern are true about ALL fields, not just ids, so with this approach, maybe we will have one value object per field.
I refer to Eric Evan's opinion in his book, when he talks about standalone objects, that making dependencies adds complexity to the code and demands more effort to understand it, so when you can express a field by a primitive type, you have the chance to eliminate one dependency, and you should definitely do it, especially when you have no logic to be encapsulated with that value.
Id and other fields are not the same. If id is wrong, the whole data will be in an invalid state.
Your domain-layer objects should not be your EF models. Breaking single responsibility principle.
8:00, 8:45 Mapping is overhead but that's the trade-off for robustness and having an anti-corruption layer.
9:15 Creating a mapping cannot introduce bugs compared to switching IDs in the function arguments which definitely will and these will be very hidden bugs.
THANK YOU. Agree with you 100%
The objects created by EF Core’s fluent API definition *is* the anti corruption layer and the mapping
@@amantinbandit is a very very very wrong use for an O/RM.
Hi, great series. In DDD we use GUID for Id type because entities ids must be unique across our domain. But in database using GUID as primary key (with clustered index) has performance issue specially in heavy insert scenarios because of randomness of GUID Ids.
What can we do about it?
There are solutions for index-friendly GUIDs such auto-incrementing GUIDs. The ABP framework e.g. implements a GUID generator that generates pseudo GUIDs for that purpose.
Use ULID based GUID
Yeah, you can change a GUID to a string, but if you change a string id to a Guid, then you might break existing string values that don't meet the Guid requirements (unless I'm misunderstanding what you're recommending).
Strongly typed Id is really great, but the problem you faced I found out is in the EFCore migration generation process. If you mention configuration instances that are not having the "OwnedMany()" methods first and then mention the configuration instances that have "OwnedMany()" methods, then "EFCore" is able to generate the perfect migration files. Maybe this behavior is happening because of using reflection heavily. However, I'm not sure about this. Maybe you and other experts could find out that and can issue a bug to the Github repo. 😊
Great stuff, thanks!
Was the correct part of the migration shown at the end? The example is about Host having many MenuIds. But we see a table of MenuDinnerIds, i.e. combining Menu and Dinner?
And it looks like you would only get one foreign key constraint, back to the "owner", i.e. to Host (or, in the shown migration to, Menu). But there is no FK constraint on the MenuId, so you can have invalid references in the database?
Generally I would expect the end result to be a join table in the database, with references to both Host and Menu. Your result ends up with only one foreign key.
Welcome back! Where were you? Missing your videos. Now, I see an upgrade in your drawings. What is the tool now? It seems it is not longer ZoomIt! Good job again!
Presentify. ZoomIt doesn't work on MacOS 😢
Which technology are you using for that slides and arrows? It's pretty awesome.
Thanks! Slides - Figma. Arrows - Presentify.
Thanks for letting me know!!! Great content.
Currently implementing this is heavy (a lot of code and relatively complex code) that it does not justify the benefits (you also need to do work on the MVC side to make it map the ids). It would be cool if C# had some kind of type aliases where you can just give names to existing types which would make it simpler to use and support in libraries like EF and MVC as they would just need to recognize the actual type and treat the value as such while the compiler takes care of wrong usage
The part that I don't understand is that you said the ID is a VO, yet a VO should not have any identity, this is the key difference between an entity and VO.
Can it be solved by Complex Types as value object in new EF8.0?
Not yet cover. Collections of complex types are not yet supportted. Vote for the issue 31237
I thought the object-type configured with OwnsMany should be recognized as a Non-Entity Type? Can someone explain cuz he said in the video that MenuId in HostAggregate config is an Entity Type
Hi Amichai! I have a question,
For queries with multiple related entities like getting the Menu with the list of Dinners and MenuReview which sits on different Aggregate Root. What do you think you'll use on that?
I'm so happy to subscribe to your Patreon. You're such a blessing in the community. :)
Also, for querying only details of an entity which is not an aggregate root. Thank you!
Mapster giving issue with record no default contractor for type RegisterCommand, Please use ConttructUsing or MapWith
Why are these ids called ValueObjects and not DomainPrimitives? Seems like a more descriptive name for them.
The best thing you can do concerning EF, is just use something that benefits your life and organization.
EF doesn't provide any additional value, only introduces new problems.
From knowing the intricacies about how the change tracker actually works, to knowing what linq is valid, what the valid linq translates to, to how easily it is for devteams to mangle the snapshot, to its obscured concurrency capabilities (that only the most knowledgeable devs know how to do), it is just never worth it.
See how I didn't even mention sql performance? I don't think sql performance along is enough to choose an ORM. It really boils down to the enormous amount of learning that needs to occur in order to maintain EF applications.
I've been working with it for over half of my career, and the development shops that wisely choose not to use EF are rewarded handsomely.
Just use something like dapper and manage migrations through fluentmigrator or something similar.
The non-EF shops ALWAYS have a much more pleasant time with data access and database maintenance. EF adds nothing but complexity and headaches
I have strongly typed my cats
I have 2 unrelated sets of tables (Menus and Hosts) after adding the Host aggregate code and adding its tables to the database. Is this how it should be? Isn't the HostMenuIds table a many to many relationship table for two aggregates Menus and Hosts? Should these two aggregates be linked, or was it originally intended to make an unlinked set of aggregate tables for further division into microservices with separate bases?
In the BuberDinner's codebase as far as I know you're supposed to raise a new domain event like "record MenuCreatedDomainEvent(MenuIn menuId, HostId hostId) : IDomainEvent". When saving changes to the database, the saving changes interceptors should publish this event and the MenuCreatedDomainEventHandler : INotificationHandler should take care of linking the MenuId to the Host. So it could look like this: var host = _hostRepository.GetById(notification.hostId), then check if host is not null, and eventually perform the linking operation: host.AssignMenuId(menuId). Remember not to call SaveChangesAsync, you don't need any unit of work in the event handler, because when all event handlers have finished their work the dbcontext is gonna save changes. I guess for a distributed system you could imagine a situation when domain event handlers publish some sort of integration event to a message queue like RabbitMq, so the other microservices can update their db state. I hope it helps!
Entity framework migrations are only good for simple project. Pretty useless in a big company which needs the DB schema to be the same in all environments. Will MenuId:AggregateRoot not work if there in a implicit cast to string? For JSON serializer we need to give a conversion function. It was in my todo list that your video now reminded me to do.
Hi! Thanks for the video. I think it's too many generics in this solution- too complex. Btw, what tool do you use to draw on the screen?
Generics usually make the code more complex and harder to understand. I don't think most applications need this kind of overhead. I use Presentify for the arrows and rectangles 🙂
@@amantinband Yes, thank you for the answer and for the great content!
First!
I am having issues migrating like this. It keeps telling me that (for example) no suitable c-tor was found for 'HostId', so I go ahead a make a paramterless one there (which is something you didn't have to do), then the error changes to the entity type 'HostId' requires a primary key.. etc.
I essentially keep going in a loop, adding `HasNoKey()` to X Id Value Object, then it says that I cannot be Keyless, and suggests making the AggregateRootId keyless, which initself brings another error, and I just cannot get it to work for whatever reason, and my project is basically 1:1 with yours.
Been debugging for a few hours now, reading stuff online, but nothing seems to be working. Would be great if anybody has suggestions.
Watch closely in the video. He’s not going from the last version to this version but rather deleted the old one and is executing “add initialCreate” again
Couldn't you make use of user-defined explicit or implicit conversation on the strongly typed IDs? To make EF core see/use the encapsulated primitive?
That won’t work either. OwnsMany/OwnsOne defines the type as an entity type
@@amantinband what do you mean by OwnsMany/OwnsOne defines the type as an entity type.. what do u mean by entity type ? this video made me so confused :(
Hi and thank your for your great afford.
I have a question! What about the MenuId column, in the HostMenuIds table? Shouldn't it be a foreign key to the Id column of the Menues table?
Currently following you toturial, I'm missing this relation!
And I think it's important to have it, in case of deleting menu, we can cascade it to delete the corresponding record in the HostMenuIds table
Thank you very very much👌
Well, I think I found the answer in the next video, Domain Events!
As you mentioned there, aggregates are transaction boundary, And we dont want changing an aggregate to have side effect on others, and that's why we don't setup foreign keys!
But this brings me to next question, so why do we try to set foriegn key for the host-menu relation as described in this video?! Deleting a host, would delete the menu... isn't a side effect?!
@@VahidRassouli This is very impractical. DDD doesn't need to be followed to the 100%. If you don't add FK, you are risking data integrity and there is no point of using relational database then. If you do not use cascade, there is no possibility of side effect.
2:00 Static method signature should probably return ReservationId?
Yup 😀
The sound was very quiet in this video.
A question here, how would you implement a many to many relationship with this approach ? for example if you have an A-aggregate who owns list and this B-Aggregate is suppose to own List this would throw the same exception as before, how can we solve this?
This should work. Are you referencing a list of IDs in both?
@@amantinband Yes I'm referencing a List of Ids in both and efcore throws the same type of exception when trying to add a migration.
Aggregate-A has List
Aggregate-B has List
@@ahmedrizk106 Perhaps try using the .NET 8 preview SDK. They fixed this error message (among others), it may give you insight to what the error is
parameter objects for the function and unit testing. problem solved.
Why coming up with some new creative ideas to clutter a project with yet another approach for already solved problems?
I've practiced domain driven with ef core for two years now without ever having to map up identities as entities. I don't understand why you would wanna do this? To me, it feels like this ain't a problem with the feature set of ef core, but some other misunderstanding on how you should model aggregates.
The IDs are value objects, not entities. If you’re interested in learning more, you can check this out: www.informit.com/articles/article.aspx?p=2020371&seqNum=4
@@amantinband I know that. Thats why i wouldnt configure them as entities, as you try to do.
Oh, perhaps I wasn't clear in the video - I am talking about EF Core entity types/non-entity types, not DDD entities.
Why make the Id property of the AggregateRootId abstract? Why not just implement it in the base class itself then you wouldn't need to override it in every inherited class.
You're right, what you're suggesting is better 🙂
@@amantinband Keep up the good work, champ!
This is pointless because if both ValueTypes take a string anyway, the mistake can still be made where a new UserId is constructed with the tenantId, and vice versa, so you just moving the issue up the pipeline, this does not solve anything
Your problem lays in the architecture. Do not use EF in your business logic and you will not face such kind of problems.
(if problem does not exist, a solution for it is not needed)
“reasons”
The codebase keeps changing between videos. If you're following along you'll struggle unless and even with being a patreon member and having access to the source code.
We've NEVER had problems identifying the IDs in our projects i feel this is another over engineering that creates more problems than it solves. The more i learn about "advanced" software development the more i learn about problems that no one ever had.
Why stop there, lets encapsulate every single property, then copy it at least 5 times. This solves a minor problem at best.
Do these techniques just get pushed so someone can sell more books?
This is not overengineering. EF Core 8 now officially supports this feature. If this is over-engineering, microsoft will not provide the feature in the first place.
I agree that this is not an usual problem to have in small teams, maybe the problem of confusing arguments could happen in a project where hundreds of people work on the same code base and they don't know each other or it's a community driven open source project. In any case, you will have other mechanisms to detect this error: unit tests, integration tests, manual tests, etc tests... code reviews, peer reviews, and some tools might give you hints that the names do not match... so I would only use the technique of having value objects for Ids when it's absolutely necessary.
In other languages like Typescript this would make more sense....
For some reason I get this exception when I use the register endpoint.
Mapster.CompileException: Error while compiling
source=BuberDinner.Application.Authentication.Common.AuthenticationResult
destination=BuberDinner.Contracts.Authentication.AuthenticationResponse
type=Map
---> System.Reflection.AmbiguousMatchException: Ambiguous match found.
...
In the mapping configs, you can enforce .ToString() on the src.MenuId.Id.Value. This makes it work as it can parse the string value to a Guid just fine.
@@GrimReaper160490 Mate, thank you so much. You made may day. I even sent Amichai an email asking for help XD
Thank you again.
How about 1-1 relationship : Ex: 1PurchaseOrder can belong to 1 Vendor
public class PurchaseOrder : AggregateRoot {
public Vendor Vendor { get; private set; }
public VendorId VendorId { get; private set; }
}
// Vendor
builder.Property(p => p.VendorId)
.HasConversion(
id => id.Value,
value => VendorId.Create(value));
// TODO
builder.HasOne(p => p.Vendor)
.WithMany()
.HasForeignKey(p => p.VendorId)
.IsRequired();
But I can not do this