Reputation: 191
I have done an IMMENSE amount of reading about domain driven design and have done some fairly complex projects with that design. All of them had their flaws and anti-patterns that were realized along the way. Understandable as this was a learning process. I, however, am stuck on a major concept that I cannot seem to resolve via Google (maybe I'm just not coming up with the right search terms) or by my own trial and error.
I have read several articles that claim emphatically to keep your Domain Model and your Persistence Model separate. Don't let things like an ID leak into your Domain Model unless there is a domain purpose for that ID. With that policy, how does one persist a Domain Model IN PRACTICE? All the articles I have read talk about this in the abstract, but I cannot find a concrete example that DOESN'T violate this.
I have a relatively large and complex web application that I'm building and would like to achieve the "best" Domain and Persistence separation I can. I am using a hand-rolled ORM (yes, yes, I know - I shouldn't - blah blah blah - however, the underlying tables and queries are much too complex to use something like EF or NHibernate well). In this accounting package at a large university, I have General Ledger Journal Entries with the following structure:
Public Class Journal
Public Property AccountCode As SFSAccountCode = Nothing
Public Property Amount As Decimal = 0
Public Property BudgetCategory As BudgetCategory = Nothing
Public Property [Date] As DateTime = Nothing
Public Property ChildAccount As ChildAccount = Nothing
Public Property Description As String = ""
Public Property FiscalYear As SFSFiscalYear = Nothing
Public Property Fund As Fund = Nothing
Public Property JournalID As Int32 = -1
Public Property Notes As String = ""
Public Property Program As String = ""
Public Property Source As JournalSource = Nothing
Public Property Status As JournalEntryStatus = JournalEntryStatus.Open
Public Property TransactionType As TransactionType = Nothing
End Class
Without including the uniqueID (JournalID), how can I map an instance in the Domain Model to an instance in the Persistence Model? From my understanding, you are to consider an object unique by its invariants. Easy, if your object is only one or two properties as strings or ints. I clearly have many properties of which several are domain models themselves.
I'm sure there is some key concept that I have just missed - can anyone point me to a resource (with concrete code as bonus!) to help explain how one can map between a persistent model with database IDs to a domain model without?
By the way, yes I know my properties should be private sets for good domain design. And they will be once I can figure out the mappings between persistence and domain better. And I happen to code in VB.NET but can read Java or C# fine, as I'm sure most examples will be in one of those two languages.
Upvotes: 8
Views: 4558
Reputation: 21
I think the answer is relatively simple in my opinion. Let's say that you don't have a persistence model, then how do you identify your domain model?
Using your own example, how do you identify your Journal object in your domain? Perhaps, the JournalID is the only choice, then your domain needs it and you should be more than happy to added to the domain model.
In other scenarios, for example, you have an Order domain model, your domain use OrderReferenceNumber (a string value) to identify it. For example, you put this number in as an identifier of the order when sending confirmation email to your customer. On the other hand, on your persistence model, you have an OrderID (a long value) as the primary key. In this case your domain model doesn't need to know about it and it should not be leaked into your domain model.
Hope this helps and if you have complex domain models and not considering EF or NHibernate, maybe try this, FluentMap.
Upvotes: 2
Reputation: 1758
You aren't required to create another persistence class out of domain class in DDD. If you read DDD example for Eric Evan's book in http://dddsample.sourceforge.net/, you can see that entities do have surrogate id in addition to natural id. For example:
package se.citerus.dddsample.domain.model.cargo;
...
public class Cargo implements Entity<Cargo> {
private TrackingId trackingId;
...
/**
* The tracking id is the identity of this entity, and is unique.
*
* @return Tracking id.
*/
public TrackingId trackingId() {
return trackingId;
}
...
Cargo() {
// Needed by Hibernate
}
// Auto-generated surrogate key
private Long id;
}
The surrogate id is generated automatically by Hibernate. This can be seen in Cargo.hbm.xml
:
<hibernate-mapping default-access="field">
<class name="se.citerus.dddsample.domain.model.cargo.Cargo" table="Cargo">
<id name="id" column="id">
<generator class="org.hibernate.id.IdentityGenerator"/>
</id>
...
</class>
</hibernate>
If you create your own ORM, perhaps you can automatically add this surrogate primary key to all entities. The surrogate primary key should be generated automatically (for example, auto-incremented) and used for relationship in tables.
Upvotes: 1
Reputation: 1151
According to Domain Driven Design:
There are entitiy objects (entities) and value objects (values). Values are identified by a set of values (or fields). Thus, if we have two value objects with same fields, they are not disinguishable for us (as model in real life). Often value objects are immutable.
Entity objects are not identified by contained values. Entity objects are identified by its own unique existance. We cannot say that two persons with same name presents single real man.
For example:
class ProductAmountPair {
public Product Product { get; set; }
public int Amount { get; set; }
}
class Order {
public int Id;
public IList<ProductAmount> { get; set; }
}
ProductAmount is a value, and Order is an entity (actually it depends on concrete case).
To identify entity object, it is necessary to determine some unique value (key). In some domains keys are given externally (from real life), but another ones requires to generate it and thus application itself defines a way to do that.
So, conclusion is: for entities we have to introduce key field to identify them. It is necessary since entities cannot be identified by a set of values they contain.
Ok, what about database and its role.
Today databases are not just only data storage, but are complicated mechanism for ensuring of the data integrity, transactins processing, etc. Very often complex tasks like concurrent access are solved by applications just by "broadcasting" them to database.
By the same reasons databases are often used to generate key values, they propose good reliable mechanism to obtain new unique values. So, keys are generated by database because it is easy to implement, and keys are used to map domain model entities to persistance model because it is natural.
One can say, that DB engines requires to create primary keys for each table, and thus every class in domain should have key field, and thus it is entity. But:
Why should we consider databases (even relational databases!) when talking about domains? In his DDD book Eric Evans says "do not confront with technologies and paradigms".
Sometimes, because of strong performance or realiablity requirements we have to give up some DDD purity and clearness and make domain model more (for example) "relational" (I mean class-table mappings mostly).
We just have to deal with it.
For same reasons we can admit a partially "techincal" role of key field of entity. But also we admit its main role as domain identification attribute.
Upvotes: 5