Having Separate Domain Model and Persistence Model in DDD
Asked Answered
M

3

89

I have been reading about domain driven design and how to implement it while using code first approach for generating a database. From what I've read and researched there are two opinions around this subject:

  1. Have 1 class that serves both as a domain model and a persistence model

  2. Have 2 different classes, one implementing the domain logic and one used for a code-first approach

Now I know opinion 1) is said to simplify small solutions that do not have many differences between the domain and persistence models but I think it breaks the single responsibility principle and by that introduces a lot of issues when an ORM's conventions interfere with DDD.

What is a surprise to me is there are numerous code examples of how to implement opinion 1). But a haven't found a single example of how to implement opinion 2) and how to map the 2 objects. (Probably there are such examples but I failed to find a C# one)

So I tried to implement an example on my own but I am not sure if that's a good way to do it.

Let's say I have a ticketing system and tickets have expiration date. My domain model will look like this:

/// <summary>
/// Domain Model
/// </summary>
public class TicketEntity
{
    public int Id { get; private set; }

    public decimal Cost { get; private set; }

    public DateTime ExpiryDate { get; private set; }

    public TicketEntity(int id, decimal cost, DateTime expiryDate)
    {
        this.Id = id;
        this.Cost = cost;
        this.ExpiryDate = expiryDate;
    }

    public bool IsTicketExpired()
    {
        if (DateTime.Now > this.ExpiryDate)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

The persistence model using Entity Framework as ORM will look almost the same but as the solution grows this might not be the case

/// <summary>
/// ORM code first Persistence Model
/// </summary>
public class Ticket
{
    [Key]
    public int Id { get; set; }

    public decimal Cost { get; set; }

    public DateTime ExpiryDate { get; set; }
}

Everything looking great so far. Now what I am not sure about is which is the best place to get a Ticket persistence model from the repository and how to map it to the TicketEntity domain model

I have done this in an application/service layer.

public class ApplicationService
{
    private ITicketsRepository ticketsRepository;

    public ApplicationService(ITicketsRepository ticketsRepository)
    {
        this.ticketsRepository = ticketsRepository;
    }

    public bool IsTicketExpired(int ticketId)
    {
        Ticket persistanceModel = this.ticketsRepository.GetById(ticketId);
        TicketEntity domainModel = new TicketEntity(
            persistanceModel.Id,
            persistanceModel.Cost,
            persistanceModel.ExpiryDate);

        return domainModel.IsTicketExpired();
    }
}

My questions are:

  1. Are there any reasons opinion 1) would be preferred to opinion 2) other than speeding up development and reusing code.

  2. Are there any issues in my approach of mapping the models? Is there something I missed that would bring up issues when a solution grows?

Mercerize answered 11/7, 2014 at 17:58 Comment(6)
I wish I could upvote this question 1000 times. Lovely to see someone understand the need for separation to not compromise the domain model due to ORM conventions.Coheir
Excellent question.Octillion
Why put an Id in the Entity? Aren't Entities distinguished from by their references? Isn't the Idpersistence only? What if my persistence, which should be unknown, was based on binary serialization? I wouldn't need an Id at all.Jaynejaynell
@Adrian Hristov, did you end up going with option 1 or option 2? Did you find any good examples of option 2? I agree that all the examples I find on github seem to use option 1.Photogrammetry
@Photogrammetry it's been a few years now since I posted the question so I suppose it's appropriate to share my opinion. I can say that in all the projects I've worked on in that time I've always gone for option 2. As mentioned by some of the answers below, option 1 can be seen as a violation of the Single Responsibility Principle. In my experience, any real-world project has enough differences between domain and entity classes, so even if it feels as overkill at first it's been beneficial to separate them. Of course there is overhead in persisting data etc. but I'm willing to live with it.Mercerize
An example that domain entity is used as ORM model by Oskar Dudycz: github.com/oskardudycz/EventSourcing.NetCore/blob/…Club
C
40

Are there any reasons opinion 1) would be preferred to opinion 2) other than speeding up development and reusing code.

Option 1 is just because of pure laziness and imagined increased development speed. It's true that those applications will get version 1.0 built faster. But when those developers reach version 3.0 of the application, they do not think it's so fun to maintain the application due to all compromises that they have had to do in the domain model due to the ORM mapper.

Are there any issues in my approach of mapping the models? Is there something I missed that would bring up issues when a solution grows?

Yes. The repository should be responsible of hiding the persistence mechanism. It's API should only work with domain entities and not persistence entities.

The repository is responsible of doing conversions to/from domain entities (to be able to persist them). A fetch method typically uses ADO.NET or an ORM like Entity Framework to load the database object/entity. Then convert it to the correct business entity and finally return it.

Otherwise you would force every service to have knowledge about persistence AND working with your domain model, thus having two responsibilities.

If you work with application services per the DDD definition you will probably want to look at the Command/Query separation pattern which can be a replacement of the application services. The code gets cleaner and you also get a much more lightweight API wrapping your domain model.

Coheir answered 11/7, 2014 at 20:46 Comment(12)
Hi, I'm glad someone shares my believes :). I still have some things unclear: What if I have an entity and a value object but in the database I want to present them as 2 different tables? For instance Clients and Addresses you might have 2 addresses and present them as value objects but having them in the same db table will lead to denormalization. How should the Domain and Persistance models (now having 2 of them) mapped and where? Because typically I would have a ClientsRepository and AddressesRepositoryMercerize
The ClientRepository should use the AdressesRepository as the domain layer should be persistant ignorant.Coheir
@Coheir - if repositories are responsible for say mapping the persistence model to the domain model, how do you actually map PM to DM when most the properties in DM have private setters? The OP uses constructor, but how would this scale if there are too many properties to set?Lelahleland
Too many properties to set suggests that the entity have too many responsibilities. I tend to use reflection in the repository to set properties. imho it do not violate the encapsulation since all entities are stored in the DB in a valid state (as long as nothing outside the repos violate the encapsulation)Coheir
@Coheir I fully agree with you.Cabbala
"pure laziness and imagined increased development speed" could not agree more.Gordon
@jgauffin, +1 for "So it's in the repository that the mapping should take place.". Can you confirm that you mean: 1) the repository gets the data from the database and puts it in a persistent object, 2) Inside the repository method; the persistence object is mapped to a domain object, 3) The domain object is returned by the method. Thanks.Photogrammetry
@Photogrammetry Yes, all three confirmed :)Coheir
Thank you. Can I ask what your opinion is of having a generic value type like this (when using a separate domain model and data model): github.com/vkhorikov/DddInAction/blob/master/…Photogrammetry
Works for me. Reduces complexity in all value objects.Coheir
@Coheir can you please suggest a good read around this specific topic?Valvule
@AshutoshSingh I had several articles in my blog, but it's shut down :( Here is one on the articles on the subject: web.archive.org/web/20120611035254/http://blog.gauffin.org/2012/…Coheir
R
24

I got into this dilemma this year in a big project I was working at and it was a really tough decision to make... I would like to talk about this topic during hours, but I'll resume my thoughts for you:

1) Persistence and Domain model as the same thing

If you are in a new project with a database designed from zero for it I would probablly suggest this option. Yes, the domain and your knowledge about it will change constantly and this will demand refactoring that will affect your database, but I think in most cases it's worth it.

With Entity Framework as your ORM you can almost keep your domain models entirely free of ORM concerns using fluent mappings.

Good parts:

  • Fast, easy, beautiful (if the database is designed for that problem)

Bad parts:

  • Maybe the developers starts to think twice before to do a change/refactoring in the domain fearing that it will affect the database. This fear is not good for the domain.
  • If the domain starts to diverge too much from the database you will face some difficulties to maintain the domain in harmony with the ORM. The closer to the domain the harder to configure the ORM. The closer to the ORM the dirtier the domain gets.

2) Persistence and Domain model as two separated things

It will get you free to do whatever you want with your domain. No fear of refactorings, no limitations provinients from ORM and database. I would recomend this approach for systems that deal with a legacy or bad designed database, something that will probably end messing up your domain.

Good parts:

  • Completely free to refactor the domain
  • It'll get easy to dig into another topics of DDD like Bounded Context.

Bad parts:

  • More efforts with data conversions between the layers. Development time (maybe also runtime) will get slower.

  • But the principal and, believe me, what will hurt more: You will lose the main beneffits of using an ORM! Like tracking changes. Maybe you will end up using frameworks like GraphDiff or even abandon ORM's and go to the pure ADO.NET.

Are there any issues in my approach of mapping the models?

I agree with @jgauffin: "it's in the repository that the mapping should take place". This way your Persistence models will never get out from the Repository layer, in preference no one should see those entities (unless the repository itself).

Redundancy answered 23/12, 2015 at 13:23 Comment(3)
One way to implement option 2 and keep a loose coupling between the two is have the domain publish events in the form of immutable DTO's that map back to persistence models when a domain transaction completes. That way your domains can choose to save even the events themselves so that there's a history of changes that allow you you replay events that occurred in your domain. Versioning could be implemented at the transactional level in this case.Fullerton
There is no good part in first option. Even "if the database is designed for that problem" because someday some DBA can say 'I am gonna de-normalize this relation between these two tables for speed!!' and for this simple change you have to refactor your Domain Model. Persistence Model changes should not mandate Domain Model changes, it is one of the biggest pillar of Domain Driven Design.Gordon
Yeah @KeremBaydoğan this is a risk that you take when you choose the option 1 as I discrebided in the bad parts of that option (If the domain starts to diverge too much from the database ...). But that can (even being rare) never happen. And even if it happen, until it happen you will be having the advantage of that option (mainly simplicity). Personally I am not a big fan of that option and probably would not encourage it in the most cases, but its a valid choice and it's up to you to decide if it's worth take the risk.Redundancy
D
4

Are there any reasons opinion 1) would be preferred to opinion 2) other than speeding up development and reusing code.

I can see a big one (opinionated stuff ahead) : there is no "Persistence Model". All you've got is a relational model in your database, and a Domain object model. Mapping between the two is a set of actions, not data structures. What's more, this is precisely what ORM's are supposed to do.

Most ORM's now support what they should have provided from the start -- a way to declare these actions directly in code without touching your domain entities. Entity Framework's fluent configurations for instance allow you to do that.

You may be under the impression that no persistence model = violating SRP and trampling on DDD, because many implementations you can find out there do. But it doesn't have to be like that.

Debauch answered 22/7, 2014 at 12:12 Comment(1)
exactly, I stopped using attributes entirelya and shifted to fluent API. this way domain model is free from any ef bloats. But some storage like Mongo, still wants you to do these. But As said by Eric Evans, It is a good enough design choice as it works most of the time.Sherri

© 2022 - 2024 — McMap. All rights reserved.