Learn To Love DDD-Style Strongly Typed IDs
Вставка
- Опубліковано 8 чер 2024
- Become a sponsor to access source code ► / zoranhorvat
Join Discord server with topics on C# ► codinghelmet.com/go/discord
Enroll course Beginning Object-Oriented Programming with C# ► codinghelmet.com/go/beginning...
Subscribe ► / @zoran-horvat
There are many implementations of strongly typed ID types on the Internet, but not many satisfy all of the mandatory and nice-to-have traits we expect from such a class. An ID, custom or built-in, must be an immutable value object with full value-typed semantics, supported by the underlying database and the intermediaries such as ORMs and micro-ORMs; it would be nice if it caused no additional allocations and garbage collection overhead.
This video will help you understand the steps required to design a small but powerful custom ID type that satisfies all functional and performance considerations that define a good identity.
An identity is essential to any object we persist. It spans the time between application runs and the time between creation and a later retrieval of the same persisted object. Hence, it exists in all persisted entities.
In this video, we start with autoincrement IDs and GUIDs, finding their ultimate deficiency - an ability to generate application bugs. Then, we introduce strongly-typed custom IDs that are application-generated and backed by compile-time assignment checks.
Before this short video ends, you will learn to love the simplicity, safety, and even speed of your custom identity types that are powerful, compile-time safe, and fully supported by Entity Framework Core and relational databases.
00:00 Intro
01:15 Autoincrement and GUID IDs
02:54 The Failure Caused by Using GUIDs
04:53 Developing a Custom ID Type
08:53 Outro
Thank you so much for watching! Please like, comment & share this video as it helps me a ton!! Don't forget to subscribe to my channel for more amazing videos and make sure to hit the bell icon to never miss any updates.🔥❤️
✅🔔 Become a patron ► / zoranhorvat
✅🔔 Subscribe ► / @zoran-horvat
⭐ Learn more from video courses:
Beginning Object-oriented Programming with C# ► codinghelmet.com/go/beginning...
⭐ Collections and Generics in C# ► codinghelmet.com/go/collectio...
⭐ Making Your C# Code More Object-oriented ► codinghelmet.com/go/making-yo...
▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
⭐ CONNECT WITH ME 📱👨
🌐Become a patron ► / zoranhorvat
🌐Buy me a Coffee ► ko-fi.com/zoranhorvat
🗳 Pluralsight Courses ► codinghelmet.com/go/pluralsight
📸 Udemy Courses ► codinghelmet.com/go/udemy
📸 Join me on Twitter ► / zoranh75
🌐 Read my Articles ► codinghelmet.com/articles
📸 Join me on LinkedIn ► / zoran-horvat
▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
👨 About Me 👨
Hi, I’m Zoran, I have more than 20 years of experience as a software developer, architect, team lead, and more. I have been programming in C# since its inception in the early 2000s. Since 2017 I have started publishing professional video courses at Pluralsight and Udemy and by this point, there are over 100 hours of the highest-quality videos you can watch on those platforms. On my UA-cam channel, you can find shorter video forms focused on clarifying practical issues in coding, design, and architecture of .NET applications.❤️
▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
⚡️RIGHT NOTICE:
The Copyright Laws of the United States recognize a “fair use” of copyrighted content. Section 107 of the U.S. Copyright Act states: “Notwithstanding the provisions of sections 106 and 106A, the fair use of a copyrighted work, including such use by reproduction in copies or phono records or by any other means specified by that section, for purposes such as criticism, comment, news reporting, teaching (including multiple copies for classroom use), scholarship, or research, is not an infringement of copyright." This video and our youtube channel, in general, may contain certain copyrighted works that were not specifically authorized to be used by the copyright holder(s), but which we believe in good faith are protected by federal law and the Fair use doctrine for one or more of the reasons noted above.
#csharp #dotnet #ddd - Наука та технологія
Apart from the content, which is always great, Zoran’s commentary and humour crack me up😂
Thanks for going into WHY strongly typed IDs are good
The advice bears repeating -- thanks for the continous flow of golden nuggets
Thanks!
Really enjoying your new style of videos, thanks!
What is the new style, what did I change? If it's good, I want to do more of it!
@@zoran-horvat You didn't show your face during the video 😄 But that could not be the reason^^
@@zoran-horvat I think it's just the general vibe to be honest :) it used to feel like a nice, laid-back lecture (and it was great), and now it feels smooth - the code appears swiftly and all the details and comments are displayed next to it.
Both formats are great, but this one just feels fresh
Keep posting please :)
@@MC_DarkMaster haha, cant be that ;)
Amazing story telling and practical programming techniques that work
Nice format, and great lesson. Thank you!
Really engaging video, genuinely a fun watch! Nice stuff!
Great video as always.
This was pretty much my journey with my product.
I started off with some uint for the ID, than we decided to change it into a string and back into an int, I got fed up with changing the ID all over the project everything management decided to change things around -> So I wrapped it in a class UserIdentifier. That in time turned into a struct, and after your video about records I changed it into a record, which removed quite a bit of code from the Equality functions. It's a shame but I cannot use record structs in this project, so might have to revert it back to a struct.
I really enjoy your videos and learn a lot from them, thank you!
I know the path you walked because that happened to me, too. Some critics don't realize that everything I say, I have pushed to production first at some time during my career. All this code just works.
Wonderful explanation, thank you kindly!
Derived types (like in Ada) would be really useful for this purpose. They would be essentially just an int, only the compiler would tag
them with additional type info, treating them as totally distinct types.
A wrapper type essentially behaves like a derived type.
Sounds like a good-old D&D tale)
Really clever solution 👍🏻
This is excellent!
Interesting. I was wondering what would be the difference in using a readonly struct instead of a readonly record struct
Top stuff, makes sense...
I understand that for different entities, I would need to create different strongly typed Ids. So it means also to declare the Empty properties and NewId(). Any way to avoid rewriting each time? I guess compiler generated codes?
Hi Zoran. Great video! What about when you have a DataTransferObject, say BookDto, in your API?
The DTOs should contain primitive types, because their purpose is to transfer low-definition data. Therefore, the conversion to and from a DTO would also include unwrapping and wrapping the GUID into a strongly typed ID.
This process is the same as for all other pieces of data in the DTO and it stems from the fact that the domain model is built around processes and DTOs are built around values. Putting both aspects into the domain model would cause lots of trouble everywhere, and hence we separate those.
Love it !
I included a F# project into my C# solutions with one simpe module like this ...
module IdDefinitions
open System
type BookId = BookId of Guid
type PublisherId = PublisherId of string
type AuthorId = AuthorId of int
... But it requires more effort where everybody else asumes basic types (JSON, loggign, etc.). You simply can not explain the benefits to others, until they run into problems that you had 10 years ago already.
Strongly typed IDs are part of the model, and therefore not suitable for serialisation. A rule of thumb is to perform serialization/deserialization using DTOs, and then convert them to and from the domain models, because models serve different purposes and usually hide their inner representation or use representation that suits their own responsibilities. That means the DTO should contain a plain ID, which is again converted from and to a strongly typed ID.
@@zoran-horvat It's a rule of thumb - absolutely. In the end, one always have to decide where the focus lies and have to realize that there is no right or wrong way to do something, only sensible and senseless decisions.
@@AlBex-dc6yq Precisely. You just try to model around obstacles. Every decision depends on the circumstances.
Very good! I would however add to this video a section about serialization problems (Your key will be serialized as an object by default) and other reasons why you should use a library for doing this.
You should not burden the domain model with serialization. That should be the ability of the corresponding DTOs, and DTOs, conversely, should not burden with such domain problems as stringly-typed IDs. They should only contain primitive values, such as GUID, int, or long for the ID.
Thats what Automated Tests are for so even if you are passing the wrong Id you should have an automated test asserting the expected outcome
Two questions: 1) Who's gonna automate that? 2) Do you tend to stringify models and leave their correctness to automated tests, with respect to question 1?
1. The Developer that writes the Feature.
2. Im with you on the DDD approach of introducing specific types rather than falling victim to primitive Obsession though. Just wanted to Point Out that If there is a Bug because someone passed the wrong value that should usually be caught by a unit or Integrationtest.
@@pinguincoder That's fine, so let's focus on point 1. There is one place to define a strong type and dozens where the assignments happen. You are advocating weak assignment checks, dismissing the most powerful static code analysis tool we have - the compiler - and request, say, writing 20 unit tests, plus 2 which you forget, as a better idea? Be so kind to explain to me the economy of that, excuse my language, lunacy.
You could pass the object instead of the ID, you then don't have to overengineer a solution, as you already then have type safety.
In DDD it is common practice to pass around objects and not Ids, this way you can perform other DDD functions whereas before you are limited to an ID.
I'm not following you. What object do you mean instead of the ID?
Don't forget that any type you use as the identity must map to a primitive type supported by the database.
MakeFamous(Book book)
@@djdaveydave123 Where did you get the Book object from?
I assume that this impacts JSON serialization and could make a project upgrade from Guid to record structs a bit risky if serialization is used.
In what way does it make it more risky?
I expect certain complications in serialization, mostly coming from syntax, but not from any ambiguity that could cause risks.
@@zoran-horvat Thank you for the reply and for the videos, always apreciated. Since this is not a transparent in-place update, care should be taken to test areas of the code where serialization/deserialization is used since the compiler cannot check for problems. One example is public API where responsens could be objects that use automatic mappers. If tests are present this should be easy to catch.
@@mihaiga I see your point. I'll keep it in the back of my mind for a while. Maybe something will come out.
Maybe there could be a way to interpret the wrapped GUID struct as a GUID when it comes to serialization? Maybe an implicit conversion could be used from GUID to the new strongly typed identifier?
How do you go about removing the exception that is thrown when you use entity.HasKey(e => e.Id); entity.HasComplexProperty(e => e.Id);? It appears EF doesn't support complex PK's although it looks like you have at least one in your example.
There must be the value mapping on that property. It works well with EF, I'll prepare a separate video for that.
Excellent. Many thanks.
How would this mirror to the database? Wouldn't it generate a separate table for the BookIds?
No, there must be the value mapping back to plain GUID and that would be the row key in the database.
Hi Zoran, great content as always.
What about making it generic?
public readonly record struct StronglyTypedId(Guid Value)
where T : notnull
{
public static StronglyTypedId Empty => new(Guid.Empty);
public static StronglyTypedId New => new(Guid.NewGuid());
public override string ToString()
{
return Value.ToString();
}
}
What would T be? Any class?
I see your point with this type, but I object that it is very abstract for the purpose. Using it almost looks like Yoda speak. If it is to save three lines of code per entity class, I'm not sure it is worth the mental effort associated with its use.
Anyway, on first look, I'd say it would work fine.
@@edgeofsanitysevensix I think T is the class where the Id is for. So in the Book class you would define a StronglyTypedId Id ...
A readonly record struct is a thing? Hmn, that could prevent some Heap churn.
/ *reads Bertrand Meyer, the Book* /
isn't there an allocation for GUID? aren't you making a complicated wrapper for what is essentially an enum (yea i know it's not the same, but it's only the same because you cant define enums at runtime which would only be useful if you dont know all the enums at compile time)?
Guid is a value type. There is no allocation for it.
@@zoran-horvat oh, ok, thats interesting
Interesting. But what if I need to place Books and Publishers to different APIs? Books aggregate and Publisher aggregate will be in different projects. And then it'll be difficult to use PublisherId in Books aggregate. I need to declare that type again.
Both APIs should contain the types they are using.
Interesting idea -- what I get is I need to learn about records, since they don't exist in Java or C++ and I hadn't come across them in C# before -- so until now "record" was just what Pascal call a "struct" to me. Not that I have much immediate practical use for this.
There is a previous video I made that explains C# records and their use in depth.
When my class is serialized to JSON I am getting Id : { 'Value' : 1} instead of Id : 1. Is there a way to fix this?
Strongly typed IDs are like any other type when it comes to serialization. You have to unpack the primitive values before serialization and package them back after deserialization.
The problem you are experiencing probably originates in the attempt to serialize a domain model, which is a wrong step in any settings. You should serialize DTOs instead.
Wouldn't the Empty property bites us later on, given it could produce an object in an invalid state. I'm thinking of one of your previous lessons on never construct an object in an invalid state.
It is not invalid for an ID. That is a regular case when you request the database to populate the ID upon insert, and it is backed by EF configuration. On the other hand, if you set the ID value in the entity, EF will use that in the insert and not change it.
It's worth mentioning that guid created from c# application and used as primary key clustered index in relational database significantly decrease performance.
Yes, that is why the index should not be clustered in that case.
Could you make a video about Rules Engine Pattern? And envolve it with Specification Pattern
Possibly yes.
public readonly record struct BookId(Guid Value):
> Model bound complex types must not be abstract or value types and must have a parameterless constructor. Record types must have a single primary constructor. Alternatively, give the 'Value' parameter a non-null default value.
Ok apparently entity framework doesn't do that, let's just use a record then
public record BookId(Guid Value):
> InvalidOperationException: The entity type 'Book' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'.
....
Ok, so just ignore the whole strongly typed ID thing and use int Id, got it.
Wrong.
@@zoran-horvat
Previous was with a Sqlite db, so now I made it as simple as I possibly could. Memory database, and copy straight from the video, no trying to work it into my existing project.
#1.
New WebApp, .NET 8.0
Add nuget EFCore.InMemory and dependencies
#2.
builder.Services.AddDbContext(options => options.UseInMemoryDatabase("Test"));
#3.
public class MemoryContext : DbContext {
public MemoryContext(DbContextOptions options) : base(options) { }
public DbSet Books { get; set; }
}
public class Book {
public readonly record struct BookId(Guid Value);
public BookId Id { get; set; } = new(Guid.Empty);
public string Title { get; set; } = string.Empty;
private Book() { }
}
#4. Index page
public void OnGet() {
var books = _context.Books.ToList();
}
# result:
InvalidOperationException: Property 'Book.Id' cannot be used as a key because it has type 'BookId' which does not implement 'IComparable', 'IComparable' or 'IStructuralComparable'. Use 'HasConversion' in 'OnModelCreating' to wrap 'BookId' with a type that can be compared.
There must be steps in the video that are being left out? Such as defining keys inside OnModelCreating?
@@zoran-horvat
UA-cam eats my code snips so I'll just say I fixed it by first rewriting away from sqlite to inmemory, and then figuring that inmemory has its own issues so I had to write a value converter for the key to make it work.
And because that became tedious very fast I wrote a generic identity type that I can just use on all the entities and then a modelbuilder extension hack to put in a converter easily.
It's ugly but it works. Sort of.
Why is code just problems on top of problems on top of more problems?
I'm 100% with you on that topic. The only thing I dislike is that if you want to export a model with System.Text.Json you will get an "ugly" Json from the Json-perspective because of unnecessary nesting. Should I then make a copy of that model only for Json because of that one field?
That is a good question. I might try to come up with a good answer at some time later.
Normally,, I avoid direct serialization/deserialization of domain models because that process would be ridden with issues anyway. I usually add an intermediate step, converting the model object into a DTO first, to make it serialization-friendly, among other things.
Add a custom JsonCoverter for this Type.
@@tobias989 It is not just the ID. Serializing a domain model, whose primary reason to exist is to model business processes, will always be connected to issues of all kinds. We normally use DTOs to support data-only operations and isolate the model from those duties.
@@zoran-horvatThat is a good point. The DTO can contain directly the GUID or a String representing the BookId. With this approach no custom JsonConverter is needed.
@@tobias989 The Json-Converter is no option for our projects because it's incompatible with source generation (trimming, aot, etc.).
If our domain models are acceptable to serialize then it feels so unnecessary to introduce duplicate code only because we want to introduce strong id's
Nice video! However, I wonder if this is not too much overengineering for such a small problem. Extrapolating the idea, why not creating strongly types for every other property of the Book and Author objects which are primitives and where have the same risk of passing an incorrect value?
why extrapolating? 😊
I'd say that you can push this idea to any length, and then it comes to a judgment when to stop. IDs are pervasive in entities design - every entity has one.
If you knew the Web application I added side by side with this isolated demo application, you would know that turning only the three IDs into strongly typed IDs caused changes in 20-30 other places in the model and UI.
Even in the smallest of all applications, with only two web pages, every ID is used in a dozen of places to render those two pages! You can imagine how many thousands of instances that would be in a large application. That makes this particular detail a highly cost-effective candidate for refactoring, a much better one than any other part of the design.
so ultimately what's the advantage of a strongly typed guid other than not making an obvious error?
What is the advantage of using strongly typed anything in your code base other than not making an obvious error?
Why use a compiled language other than letting the compiler report obvious errors?
@@zoran-horvatWe can't and shouldn't strongly type everything, only strongly type the things that matter. We use strings instead of enums in many places in our code because not all strings need to be strongly typed. We weigh the effort vs the benefit first. Saying everything should be strongly typed "just because" is not a good reason to do it. Efficiency isn't just in our code, it's the time spent doing something vs the benefit of the said thing. I was curious if there was any other reason to do this and the answer seems to be no.
@@auronedgevicks7739 We don't use strings instead of enums, though I can imagine some programmers do.
@@zoran-horvatbut we do because even though a string may only contain a set of known values there's no reason to make them enums unless they need to be.
@@auronedgevicks7739 I guess you do. It's not clear who you refer to as "we", though. Those I worked with in the last 10+ years are not your "we", I know that with confidence.
@2:47 I paused for a moment here to ponder, why wouldn't we just create an Author, Book tuple table / entity? I often get stuck on things and can't move on. So I'm posting my question now and I will try to answer it myself in a reply later.
Okay, so I think the answer is that for the purposes of this demo it doesn't matter. If you're stuck here get passed it. Yes you should probably have a Tuple table. Just keep watching and it will make sense.
It's about strongly typing IDs which can help to reduce runtime issues that the compiler won't prevent.
Right but now I'm wondering... Does Book ID become an entity? Hence have it's own table?
Does it work with entity framework and mssql?
It works with EF Care on SQL Server. I suppose that would make it work with MySql, too.
@@zoran-horvat Gonna need to test this out! I see this as an really good solution for generic apprach to diferent types of ID's!
I did a someid(int Value) and it said could not be mapped because the database provider does not support this type. (Sql Server)
Greate idea though.
There must be a value mapping for that to work. I will publish the next video soon, showing how strongly typed IDs work with EF Core and SQL Server. They work fine.
@@zoran-horvatAwesome! Looking forward to it.
I dont get it. Id mistakes happen in intermediate layer mostly. Strong id types protects from making mistakes in domain layer, but it still posible to make DomainAClass.(DomainAClassID(DtoBClass.ID)) mistake in binder )
The next video which is about to go live shows binding, too.
@@zoran-horvat and what if specification of GUID changes ? whats a point to make non template identity entity. you probably want to test domain run on different identity system.
ddd on c# sucks.. crappy language
@@alexeybeloushko7240 Are you more arrogant or wrong?
@@zoran-horvat about what? be more specific
how to push this concept down to api?
Through DTOs that remove mappings induced by the model, including those on IDs. A DTO would contain a plain GUID again, the same way as the database and UI already do.
this is exactly what I don't want. how to make request from client to not mix ids.
@@Kubkochan Invent a new JSON format. That is easy.
Perfect example of "the juice isn't worth the squeeze" that pushed me away from OO/DDD.
Yes, you are solving a real problem, but the cost of the solution is higher than the cost of the problem. Not just the cognitive load, but now i'll almost certainly need adaptors, generics, etc to use this. Just like I can pass 'first name' (a string) into a function that is meant for formatting 'last name', its not worth creating FirstName and LastName types to solve that problem.
The more pragmatic solution is If you really need to restrict it to the type, pass in the whole object and not just the id.
There is nothing to add to your code base after this. The solution is complete, there is absolutely nothing to add to it. No adapters, generics, nothing. Check it out yourself.
Why would you pass the whole object just for one property ? This is not recommended anywhere.
@@andreibicu5592 What is the "whole object" and what do you mean by "pass the whole object"? If you are referring to the strongly typed ID, it is a value, implemented as a struct. It is passed around the same way as you pass a GUID or an int, and it is an object to the same extent. It is just a value.
@@zoran-horvat My comment was a reply to what @adam said. I totally agree with you.
@@andreibicu5592 Ah, sorry, it looked to me as the same commenter after a glance... My bad.
What we gain from this aproach?
Developer cannot assign wrong id accidentally. Which can easily be detected by unit tests... Not much benefit + more complexity...
You said it all: "... easily detected by unit tests."
Compilation is the static code analysis, whereas unit tests are dynamic analysis. SCA is always preferred for the simplest of all reasons: its conclusions are universally correct. Unit tests cannot achieve that.
If unit tests were the answer, we would let go of compilers long ago.
Why can't the MakeThisBookFamous method take a Book instead of a UUID and then we wouldn't even have that problem to solve? In your example, MakeThisBookFamous may be doing too much. I mean, it doesn't only make a book famous, but it also fetches a book from a database... which can fail... We're kinda mixing some internal business logic about how to make books famous with how books are persisted in our system... I don't understand... :(
You have already answered your question. It is one of the methods that receives the ID and must load the object from the database. Every deserialization point, endpoint, controller and whatnot is precisely in that situation.
Don't forget that in any model of a significant complexity you won't be loading all the objects until it becomes clear which object is the right subject. Most of the methods in an application would instead have access to numerous IDs, only sometimes deciding to materialize an ID into an object.
Thanks for the video.
The strongly typed Ids is a valid idea for protecting against mix of ids.
But Ids in the domain entities is a design smell. They are persistence logic implementation details that have no relation to your domain. Use straight references instead. So "Book Book" instead of "Guid BookId".
You cannot use the Book at the outer parts of the system. The UI and network will only communicate the ID.
Also, it is not realistic that you will have numerous entities fully populated when only working with one of them. It is reasonable to move only the references to other entities around in operations that don't deal with them directly, not to load the entire objects in every operation.
Ids are for the application layer. Not the domain model. The domain entities refer to each other by type.
Regarding loading of the object graph: That is why you have "Lazy loading". That is one of the main benefits of NHibernate and EF.
@@AndersBaumann That looks like a religious view on DDD and I cannot go much into depth commenting it. As an engineer, I know that I should not be querying a dozen times via lazy loading only to obtain a dozen other entities I don't plan to apply the current command to. That makes the models and all operations on them bloated with navigation properties nobody actually uses, all in the name of a very abstract goal of not seeing an ID, like ID is a separate concern.
The original works by Eric Evans had defined IDs and explained their role. I agree with that explanation, it is correct. Eric has later even explained why entities cannot implement equality and yet I see everybody around start an entity by implementing its equality, even before defining its attributes. That is so wrong, as many other practices we see today are.
Actually referencing entities that do not belong to each other is a code smell.
Good luck to your code complexity and perfomance with lazy loading.
Ids are fine.
There are many many other "trade-offs" when it comes to DDD and micro-services communication, where certain domain classes use things, that typically belong to infrastructure.
Even tho I dont like Zoran's code style as much, this video is a really really good advice that makes code easier to read/write.
There is a foreign key reference so obviously they belong to each other.
"Code complexity"? With ids you cannot unit test your domain model in isolation and you will move domain logic into the application layer. Seems like something that could quickly turn ugly if you are more than a few developers.
This is called overengineering, ladies and gents
But of course.
00:42 A hash code, as per definition, does not qualify as identifier. Uniqueness is not guaranteed.
You missed the point. The number generated by the runtime is unique, and even referred to as identity. The fact it is used in GetHashCode doesn't change that.
This can lead to class proliferation problem
I think it outweighs primitive obsession problem =P
How?
If you have a complex system with huge number of entities heaving a complex type to represent identity for each entity separately you will come up with huge number of small classes.
But again, this is something related to big monolith projects. DDD on the other side tries to force you to split things in small bounded-contexts with relatively small number of entities. @@zoran-horvat
Omg, write a lot of waste code for developers, who can’t read the name of method ((
You can't imagine how short-sighted that view is.
If the compiler is already doing the static code analysis, then why not let it tell the bugs back through the same process? You sound like you lost a bit of pride if you didn't do it yourself, do you?
I remember an event when 70 people died and a certain programmer expressed that same attitude towards the guy who made the critical bug. Yet, the issue could have been prevented by the .NET runtime, if only it were in place.
In my last large project we have spent a lot of time to remove this garbage and replace with generic logic in many places of code. It decrease memory consumption like 1,5 gb in every of 4 solutions and increase development speed for 3 times.
@@1235663 You didn't watch the video, did you?
What memory consumption are you talking about? It is identical before and after, to a byte.
@@zoran-horvat you change int ids (or guid) to structs with values. this have it's consuption. We had for every objects(not only ids) legacy for every sub entities (some was structs, some classes). remove this approach help to decrease consuption (as i said before). on of reason was that we have very large caches (for calculation reasons). so some services decrease from 6 gb to 4.5. the main questions is that benefit of this approach is very arguable
@@1235663 Stepping from int to GUID is driven by other reasons, as int is not sufficient to index the objects we use today, nor does it work in distributed systems, unless the price is paid in redesigning the system in other ways. Therefore, int or GUID is the false dilemma.
After we get to using the long key, that is it. Whether it is primitive or a strong type doesn't matter. The amount of memory used in either case is identical, to a byte.
If you tell me that you have reduced the amount of memory used by 25% by only shortening the ID, then where are the data besides the ID? I mean, there must be a reference to each object as well, that is another 1.5GB. What are you telling me, that one half of your database are the keys? That is either not true or your service is one in a thousand and whatever conclusions you make from it are irrelevant in the general case. Your numbers just don't add up.
So you're writing more code because you didn't check what you were doing or name the parameter something useful like bookId? No wonder you got it wrong
Three lines you count as more code but checking what you are doing is for free.
You know better than that.
@@zoran-horvat per Id per Entity. And it's a lot more than 3 lines. I know because I managed to persuade the team to not overengineer solutions because of primitive envy.
@@TheDiggidee Not per ID per entity but one per entity, located in the same file with the entity, for clarity. That does not qualify as overengineering. The solution is adapted to static code analysis like so many other parts of the model and even language syntax.
@zoran-horvat yes it is per I'd per entity. Put the damn code wherever you want. Doesn't make it cleaner or solve the problem that you put the wrong id in. Just makes it clearer that you did. This problem could be solved by writing a unit test and double checking your work. The entire problem is down to bad naming conventions and not checking. Therefore it's overengineering because you're doing more work when you could just check.