DDD: Primary keys (Ids) and ORMs (for example, NHibernate)
Asked Answered
I

9

18

Why is it considered OK to have an Id field in the domain entities? I have seen several solutions that provide base class with Id and Id-based GetHashCode/Equals.

My understanding of domain model is that it should contain only things related to the domain. While in rare cases (trackable orders) Ids are meaningful, most of the time they do not provide anything except a simple way to reference objects in DB/on UI.

I do not see a Equals/GetHashCode benefits either, since the Identity Map implementation should guarantee that reference equality is the id equality anyway.

Strangely, I can not easily find what other people think on this subject, so I am asking it here. What is the general opinion on using non-domain related Ids in the domain entities? And are there any problems with NHibernate if I do not add Ids to my domain entities?

UPDATE:

Thanks for the answers.

Several of them suggest that having Id is the only way for the ORM to do a DB update. I do not think this is the case. ORM already keeps track of all entities loaded from the DB, so it should be easily able to get an Id internally when it needs one.

UPDATE 2:

Answer to Justice and similar points: What if we have a web application and need a way to reference the entity between sessions? Like edit/resource/id?

Well, I see this as a specific need of the constrained UI/environment, not a domain model need. Having an application service or repository with GetIdentitity method (consistent with Load(identity) method) seems to be enough for this scenario.

Ivon answered 13/5, 2009 at 20:19 Comment(3)
How would you implement GetIdentity if you don't have an identifier? You would get the object from the client, and you had to lookup the identity. How would you find it? The object's contents could have been changed on the client. You wouldn't find it anymore. I also answered to your comments to my answer.Korikorie
I am not referring to the fact when I get an object from the client. In this case I probably get some DTO that has an Id (and this is relevant even if I do not have a DB and store everything in memory, in this case Id in DTO may be a key in some internal Hashtable). But before sending object to the client, I can get an Id and add it to the DTO I am sending. Or just send an Id in some cases.Ivon
I just found in the NH documentation that NH doesn't need an id property! See my answer, I added a section.Korikorie
K
4

I just can talk about NHibernate. There you need a field for the primary key, it's up to you if you take business data (not recommended) or a surrogate key with no business meaning.

Typical scenarios are:

  • auto-incrementing value generated by the database
  • guid generated by NHibernate
  • guid generated by the business logic

There is a big advantage to have a unique id. When you pass your object around, for instance to the client and back to the server, you can't rely on memory identity. You could even have several instances in memory of the same business instance. The business instance is identified by the id.

Id-based comparison is a matter of taste. Personally I like simple and reliable code, and id-based comparison is simple and reliable, while deep-comparison is always a bit risky, error-prone, unmaintainable. So you end up with operator == comparing the memory identity and Equals comparing the business (and database) identity.

NHibernate is the less intrusive way to map a class model to a relational database I know. In our large project we have primary keys and version properties (they are used for optimistic locking). You could argue that this is intrusive, because it is not used for the business logic.

NH doesn't require to have these properties. (however it needs one of the properties as primary key.) But consider:

  • It just works better, eg. pessimistic locking is only possible with a appropriate property,
  • faster: int id's perform better on many databases then other data types
  • and easier: taking properties with a meaning to the business are discouraged for several reasons.

So why making your life harder than necessary?


Edit: Just read the documentation and found that NHibernate does not need an id property in the persistent class! If you don't have an indentifier, you can't use some features. It is recommended to have it, it just makes your life easier.

Korikorie answered 13/5, 2009 at 22:13 Comment(4)
The client-server passing is a specific use case -- it is solvable by having an identity provider or repository methods that are used for this specific case (GetId(object), GetById(id)). Also, I do not see difference between memory identity and business identity if we use an ORM. As far as I understand, within a session there will always be a single object with a given Id (except projections, but these are not interesting to compare anyway).Ivon
I see your point why business data key is not recommended, but my original question is about using a surrogate key -- but only in DB, where it belongs. So the disadvantages should not apply if NHibernate knows the surrogate DB key -- I just do not want to define it in my domain entities.Ivon
You mean: IF you don't have a client-server application, AND never have to pass objects from one session to another (eg. transfer over time, network, file) AND don't need a surrogate business identification, THEN could NHibernate manage the database primary key internally. This is probably true, but this would be a very rare case with naturally strong limitations. You could propose this as feature enhancement in NHibernate's Jira. Or implement it as a patch.Korikorie
Well as far as I understand transferring domain objects is not recommended anyway -- that's what DTOs are for. And the most time when I have a web application I see having ids as infrastructural concern, delegatable to infrastructure -- like repository.GetId(entity). We already do the same thing when we want the object back anyway, it is repository.Load(), not entity.Load(). So why is it entity.Id? I know that it is possible to add with a patch, I was just more interested whether there is some important architectural reason why using Ids is preferred.Ivon
A
3

Typically, you will not have the one true domain model. You will have innumerable concurrent and sequential sessions, which must be coordinated, and each of which contains within it its own miniature, isolated, transactional domain model. You will not have the same object instances between two concurrent or between two sequential sessions. But you will need some way to ensure that you can move information from one session to another.

An example, of course, is a /resource/list and /resource/show/id pair of URL's (corresponding to an appropriate pair of controller actions in asp.net mvc). Moving from one to the other, there are two completely independent sessions, but they need to communicate (through HTTP and the browser) with each other.

Even without using a database, your domain model objects will still need some form of identity because you will still have numerous miniature, isolated domain models, not the one true domain model; and you will have all these domain models in independent sessions both at the same time and one after another.

Object sameness does not suffice, because objects are not the same between sessions even though the underlying conceptual entities which they represent are the same entity.

Allisan answered 13/5, 2009 at 22:54 Comment(2)
Thanks for the well described point. See post update 2. I agree that you need a serializable reference/identity system if you want to do /resource/show/id, but it seems like a Web-specific constraint, not a domain requirement. But as for equality, it does not seem to me that there is a reason to compare objects from different sessions.Ivon
The unit-of-work or session pattern would require that, even in a desktop app without a database, your domain model objects have unique persistent identifiers which are the same across sessions, even though the objects themselves are not the same across sessions.Allisan
F
1

In Nh, you can't use reference equality for detached instances. This stuffs you for using NH in clients that would like to use this behaviour (in my opinion this is the correct behaviour, NH doesn't depend on magic!) .

In web apps or session based usages where you can go to the db in the scope of one ISession, I think I am correct in saying you can rely on reference equality. But in a smart client scenario, or more specifically, any scenario in which two ISessions are sharing an instance (one loaded from or inserted into the db, then passed to the second), you need the db id field(s) stored along with the instance.

the second session below would no idea that the instance that it is being passed is has already been inserted and requires an update, and even if you changed it to an update, I assume I am right in thinking it is not going to know the DB ID of the class.

class SomeEntityWithNoId
{
public  blah etc {}
}

class dao
{

void DateMethod()
{
var s1 = sessionFactory.OpenSession();
var instance = new SomeEntityWithNoId();
instance.Blah = "First time, you need to insert";
s1.Save(s1); //now instance is persisted, has a DB ID such as identity or Hilo or whatever
s1.close();
var s2 = sessionFactory.OpenSession();
s2.Blah = "Now update, going to find that pretty hard as the 2nd session won't know what ID to use for the UPDATE statement";

s2.Update(instance); //results in boom! exception, the second session has no idea *how* to update this instance.




}
}

If you are concerned about the ID field(s), which you are correct in thinking is largely useless for anything other than persistence concerns, you can make them private, so at least this behaviour and semantics are encapsulated. You might find that this approach puts some friction into TDD.

I think you are correct about the ID maps with regards to NH at least. If the instance is transient when it is first attached to the session, your type does not need to store its db ID in a field. I think the session will know how to manage it's relationship with the db. I'm sure you can bust out a test to prove this behaviour ;p

Frit answered 13/5, 2009 at 22:41 Comment(1)
I agree with your post, but I do not think the provided example (2 subsequent sessions with one object) a common use case for the NHibernate. Sending objects from server to client is another story, but in this case they might be DTO or have a custom serializer that allows Id to be sent/received.Ivon
G
0

You have to have it one way or another so the ORM can go back to the database to perform updates; I guess it could be private and hidden in a base class but that kinda violates the POCO concept.

Grille answered 13/5, 2009 at 20:25 Comment(3)
See the post update, I think the ORM knows Id anyway, it can just use the Identity Map in reverse.Ivon
You're saying that the ORM keeps track of all objects currently in the system? I can't say for a fact but it feels unlikely.Calliper
Within a session, yes, it should. Depending on configuration, session may be a web request or even an entire thread or application lifetime. The reason is that when you call SubmitChanges it has to know what the changes where. And AFAIK most ORMs implement martinfowler.com/eaaCatalog/identityMap.html as well which also means that they have to know what they already have.Ivon
H
0

assuming that your id is your primary key, you will not be able to save any data back to the DB without it.
As far as equals and GetHashCode go, these are for ordering in Lists where you create a custom ordering.

Thanks for the answers.

Several of them suggest that having Id is the only way for the ORM to do a DB update. I do not think this is the case. ORM already keeps track of all entities loaded from the DB, so it should be easily able to get an Id internally when it needs one.

I would be very surprised if that were the case. Usually it creates an insert statement only from the properties you've defined.

Haughty answered 13/5, 2009 at 20:26 Comment(1)
<i>I would be very surprised if that were the case. Usually it creates an insert statement only from the properties you've defined.</i> Yes, because in most cases it makes sense. However, a good ORM also has a previous version of your object and in general can store as much information about your object as it wants. I just googled up an example on how additional data can be added to an insert: dav-evans.com/?p=75.Ivon
M
0

There are no other option to get the same object without identity. For example people have social security number or personal code or something that is unique and we can identify them by it.

You might use natural identity but that is error prone.

Just rename Id to Identity. It looks nicer in domain model.

Entities are called entities because they have identity.

Mispickel answered 13/5, 2009 at 20:30 Comment(1)
For Web situations and other similar cases, some kind of IIdentityService that might actually be NHibernateIdentityService will be sufficient (but it is a specific case when you want to reference some specific object for future retrieval, not generic enough for domain model).Ivon
T
0

I typically include the Id in the domain as a base Entity class. I don't know of any other way to use an ORM.

An object in the domain is already unique by its reference, using an id to determine uniqueness/equality isn't really that different in practice. I haven't encountered a serious side effect to this approach so far.

Twinned answered 13/5, 2009 at 20:35 Comment(0)
B
0

I know this is a reaaaaally old post, but I stumbled on it while searching for some Domain Driven Design patterns and saw that this post could need another point of view not mentioned here for all the people who ask themselves OPs question:

Why is it considered OK to have an Id field in the domain entities?

Well, lets see what is considered an Entity:

Domain-driven design recognizes multiple kinds of models.

For example, an entity is an object defined not by its attributes, but its identity.

Quote from https://en.wikipedia.org/wiki/Domain-driven_design

So if your Business Object is an Entity it has to have an id that is immutable and globally unique. That is why business entities need an Id. On the other hand there are value objects, which by definition are identified by the values of their attributes. So they do not need a distinct id.

So why do in many models people write Ids to all their business classes? Usually to save them in the database which leads us to completely different topic: Persistence.

And in this case OPs feeling that putting an id into every business class feels wrong is correct. It is just bad application architecture design that leaks implementation details (how to persist data) into the domain model (by using primary keys in business classes).

So my answer: There are a lot of Ids in business objects because people do not separate their domain from their implementation thus keep mixing both. It is just bad application design. I personally think this comes from the heavy usage and misconceptions of the usage of ORMs. They make it easy to bring your database scheme to your OOP application but often people misuse it, might it be, because they do not know better or because it is just easier operate on the database objects given from an OR-mapper (the latter is often seen together with other common anti patterns like anemic domain model, mutable objects and / or spreading business logic across the application like putting it into controllers or even worse into the OR-mapping types).

So my answer to the question:

So why do in many models people write Ids to all their business classes?

Together with your statement:

My understanding of domain model is that it should contain only things related to the domain.

is as follows: An id (as an Identity) is via definition mandatory for an Entity in DDD but must not be provided to value objects, whose identity is defined by their attributes.

And my advice for people struggling with DDD and OR-mappers (Hibernate, EntityFramework[Core], you name it): Separate your Business Model from your Database Model. In fact these two correlate a lot, but they are definetly not the same. Do not put business logic into your database model. Put it where it belongs: In methods of your Business Classes (which gives them behavior) or in your application services that operate on your business objects and use the OR-mapper as what it was meant for: mapping Data to OOP Objects. That also prevents the anemic domain model anti pattern. A good separation of business model and implementation makes it by the way possible to switch to another implementation.

Links and more information about DDD:

Vaugn Vernon - Domain-driven design distilled (ISBN-10: 0134434420; ISBN-13: 978-0134434421)

https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

https://en.wikipedia.org/wiki/Domain-driven_design

https://en.wikipedia.org/wiki/Anemic_domain_model

https://www.yegor256.com/2014/12/01/orm-offensive-anti-pattern.html

Burgonet answered 7/4, 2022 at 8:51 Comment(0)
D
-1

You could also use multiple fields to establish identity, called composite keys. This will require more work on your end, however.

Learn about them here

Danyelldanyelle answered 13/5, 2009 at 20:39 Comment(2)
Does it mean that I will have to have the same primary key in the table?Ivon
Well, yes, the identity in nhib needs to be the identity in the db.. that's sort of the point..Danyelldanyelle

© 2022 - 2024 — McMap. All rights reserved.