今日推荐英文原文：《Don’t Use Database Generated IDs》
今日推荐英文原文：《Don’t Use Database Generated IDs》作者：Nicklas Millard
推荐理由：把应用程序需要用到的 ID 交给应用程序自己解决
Don’t Use Database Generated IDs
Stop letting the database be in charge of your applicationYou’ve probably let databases generate IDs for your application at least once.
But, what if I told you it’s a terrible idea when developing applications? Using auto-incremented integer IDs is the worst.
Lets unlearn this horrific practice once and for all.
I’m sure this stands in sharp contrast to what you learned in the Relational Databases 101 college course and the countless youtube tutorials you’ve watched when learning how toCREATE TABLE UsingTerribleIds ().
The key message is so important I’ll just write it here at the beginning in case you don’t have time to read the whole story.
- Generate your IDs at the application level. Not at the persistence level.
Or, stay put if you want to know more about why database generated IDs are awful and want to learn how I approach ID generation at client projects.
So, what’s the issue with having the database generate your application’s IDs?1 What’s most problematic is you’re delegating an extremely important aspect of your application to third party software. You lose control of your application the moment you off-load this responsibility.
2 You’re likely to apply some bad practices while designing your entity classes just to make it easier working with a persistence framework, such as EntityFramework in C# .NET.
One of the worst offenders I see junior programmers do is having public ID setters.
Just terrible to witness.
3 You’re making unit testing more difficult than it needs to be. You suddenly rely on a third party providing your entities with IDs.
Say you’ve figured that a public ID setter is essentially a terrible sin, and you don’t want calling code to set the ID either. Your class would instead look something like this below.
Your ORM of choice is possibly still able to set the id field through reflection — you know, nothing is really safe from reflection…
But, how would you unit test this? The id field is set to 0 on instantiation. Instantiating more than one TerribleBook would leave you with identity collision, as now more than one TerribleBook has the same id, even tho your should represent two separate entities.
How do we then generate more suitable IDs and reclaim responsibility?The solution is honestly dead simple. Just have a look at the snippet below.
Let’s walk thru this code, as every change from the TerribleBook to FixedBook might not be obvious to everyone.
First off, the ID is now a string instead of an integer. This allows better scalability. But remember limiting the length of the field in the database. Don’t ever use VARCHAR(MAX) for fields with a known length — it’ll eat memory.
Then, we make the constructor private and instantiate new objects using the static factory method. This allows us to abstract the instantiation logic from the caller and even provides us the opportunity to use polymorphism — we might want to return a Null Object instead of throwing.
Notice we’re still taking the id as a constructor parameter. But we are in charge of generating and providing the id (on line 18). Not the database.
The Guid.NewGuid().ToString(“D”) just ensures we’ll get a hyphen formatted GUID. I like using GUIDs, but you’re free to build your own ids which ever way makes sense to your business and application needs.
Now we’re back in control.
“But entities will no longer be stored in sequence!”You’re completely right about that. But why is that even a concern? I know junior developers like to see their entities being stored in a sequenced manner — even if it often has no business impact.
If you’d really need to store things in sequence, just provide a Created datetime column.