Eric Dieckman
Eric Dieckman

Reputation: 191

Mapping between the persistence model and the domain model

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

Answers (3)

tech forum
tech forum

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

jocki
jocki

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

Valentin P.
Valentin P.

Reputation: 1151

According to Domain Driven Design:

Some words about entities and values

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.

Databases

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:

  • Relational DB engines uses primary key for referencing and it should not affect domain model in any way
  • For real value types (like depicted ProductAmount class) you should not use stored in DB key to obtain corresponding object from database. It has no sense because value objects cannot be identified by some key (by definition of value type). But key stored in database is used to load object associations. So this operation must be encapsulated by ORM. Moreover the only way to get value object from database is to obtain it from field of some entity.

Persistance and domain

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

Related Questions