1

I was reading this question here: Having Separate Domain Model and Persistence Model in DDD

and specifically looking at this code:

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();
    }
}

This code means there is a separate Domain Model and Persistence Model. I am trying to understand what the limitations of using this approach are. All over the Internet I read about change tracking being an issue when using NHibernate, however I do not understand why. Change Tracking is dealt with after the domain model is mapped back to the persistence model. How is change tracking an issue? A practical example of how change tracking is an issue would help me.

Update Please see the code below:

//Repository
public Ticket GetTicket(int ticketId)
{
    return this.ticketsRepository.GetById(ticketId);
}

and I do this in the application service:

//Application Service
Ticket ticket = applicationService.GetTicket(1);
ticket.Cost = .....
TicketEntity ticketEntity = AutoMapper.Map<TicketEntity>(ticket);
ticketEntity.DomainMethod();
ticket = AutoMapper.Map<Ticket>(ticketEntity);

Q1) Are the benefits of an ORM lost in this code e.g. change tracking? Notice that the persistence object is returned from the repository and then is mapped to a domain object and then back to the same persistence object.

Q2) How does NHibernate track changes i.e. how does it know that Ticket(persistence object) is ticket 1 in the database. I guess it is not simply by the ID.

Amit Joshi
  • 15,448
  • 21
  • 77
  • 141
w0051977
  • 15,099
  • 32
  • 152
  • 329
  • Maybe late but if anyone is interested on seeing an example of using DDD with separate domain & persistence models while still keeping the benefits of EntityFramework's Change Tracking feature you can refer to this article I wrote: [Change Tracking while doing DDD](https://medium.com/@ledjon52/change-tracking-while-doing-ddd-ca941ee5d71f) Source code is available at: [GitHub](https://github.com/ledjon-behluli/SeparateDMAndPmWithTracking) Yes i know it's not NHibernate but it might be helpful to a user of EntityFramework. – ledjon behluli Aug 02 '20 at 19:29

3 Answers3

2

Change tracking is no issue. The mingling of Domain with Persistence is. The 'domain model' is mainly some data structure easily mapped to a table. In many domains, you might deal 99% with data structures with some rules attached. In those cases, your Domain model will look pretty much identical to Persistence model.

But going up a bit, at a more abstract level, the Domain Model models primarily business behaviour with state (data) being just an artifact. Further more, it looks at things from the business point of view (functionality).

Persistence model is about stored state i.e data structured in such a way that's easily retrievable. For domains with complex functionality that involves many concepts and their specific models and use case specific business rules, the resulted Model is quite different from the Persisted state.

How an ORM tracks changes is just an implementation detail that has nothing to do with DDD, however if the domain is rich enough, the simplistic CRUD solution and especially the mindset of domain model = state+behaviour a.k.a classes becomes an obstacle. With or without the ORM.

For the apps where the Domain = 98% Persistence Models, there's no problem, you can use whatever ORM you want.

MikeSW
  • 16,140
  • 3
  • 39
  • 53
  • Please see this link (one of many). It says: "you are not able to benefit from the built in change tracking" (of the orm). Can you comment on this? Thanks. – w0051977 Jan 04 '18 at 00:56
  • @w0051977 Change tracking is a feature of the ORM. If, for example, you're using CQRS, you'll never need that feature. Same with Event Sourcing. ORMs work best when you're dealing with mostly CRUD domains. – MikeSW Jan 04 '18 at 01:09
  • +1 for the reference to CQRS and Event Sourcing. Could you explain why you do not need change tracking with them? – w0051977 Jan 04 '18 at 17:21
  • With CQRS I believe it is because you have repositories that return persistence objects and repositories that return domain objects. Is that correct? What if you wanted to extract from the database, perform some domain logic on the object and then write back to the database. I guess my edit is relevant in this scenario? – w0051977 Jan 04 '18 at 17:23
1

Most of the details in answer from @MikeSW are correct; I only disagree with change tracking. My answer is more in terms of NHibernate than DDD.

Yes, change tracking will be an issue but it depends on how the ISession is managed. Further, not only change tracking, it will also affect other features of NHibernate like Session Level Cache, Lazy Loading etc.

Let us assume that ISession is managed on request level i.e. one ISession per request. And all activities mentioned below are part of one single request.

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

    return domainModel;
}

public void SaveTicket(TicketEntity ticketEntity)
{
    Ticket ticket = //Here, you have to map TicketEntity to Ticket
    this.ticketsRepository.Save(ticket);
}

Now, following is the code somewhere in application in same request:

TicketEntity ticketEntity = applicationService.GetTicket(1);
ticketEntity.Cost = .....
.....
.....
applicationService.SaveTicket(ticketEntity);

NHibernate have ability to track changes happen in Ticket but that ability is not useful here. Ticket is lost while returning from GetTicket and new Ticket is created while SaveTicket. Change tracking feature of NHibernate is not used at all even though ISession was at request level and was able to see the changes happen.

Following code (which bypasses domain models) will track the changes properly though:

public Ticket GetTicket(int ticketId)
{
    return this.ticketsRepository.GetById(ticketId);
}

Following is how you get and modify Ticket:

Ticket ticket = applicationService.GetTicket(1);
ticket.Cost = .....
.....
.....

Now, you do not call SaveTicket; instead you Flush the ISession somewhere in your application where you detect the EndOfRequest.

In this scenario, change tracking of NHibernate tracks the changes done to Ticket and flushes those automatically.

By translating persistence model to domain model, we bypass this ability of NHibernate because persistence model is never changed.

There are benefits and drawbacks of each approach. Refer this question.

Edit: (for your Update)

Q1): New code will take benefit of change tracking if same instance of persistence model is modified and visible to same ISession. It will also take benefit of Session Level Cache in that case. While mapping with AutoMapper, NHibernate will load referenced entities if any those may not be needed. This depends on each use-case though.

Q2): Actually this should be a different question being too broad to answer in this answer. Anyway, refer this.

Amit Joshi
  • 15,448
  • 21
  • 77
  • 141
  • I can see how change tracking can work with your second approach (where the repository returns a persistence object rather than a domain object). I guess I could return a persistence object from the repository for CRUD operations and a domain object for domain operations? – w0051977 Jan 04 '18 at 10:22
  • I personally use the first approach (separate domain models) myself where of-course domain logic will go into domain models. With second approach, even though we get benefits from features of NHibernate there are many other problems including where should domain logic go. Refer the question I mentioned in my answer. – Amit Joshi Jan 04 '18 at 10:31
  • Thanks, with regards to Q2; I saw your linked question yesterday. I am trying to understand how NHibernate knows that my object: ticket relates to Object[] in the ISession. – w0051977 Jan 04 '18 at 11:13
  • @w0051977: I did some research on this. It is the identifier property (decorated with `id` attribute) of your persistence model. NHibernate does not allow attaching two (or more) persistent models with same identifier to same `ISession`; it throws `NonUniqueObjectException` in that case. This also involves many other concepts like "Dirty Check", "Persistence Context" etc. Persistence Context is maintained per `ISession`. It will be too broad to cover all this in one answer. – Amit Joshi Jan 04 '18 at 13:05
1

On a side note, I would not recommand to separate domain model classes from persistence objects.

You might want to see what Vaughn Vernon did for his application on DOT.NET

He is the author of the famous book Implementing Domain Driven Design (IDDD). A must read that I recommand to any developer serious about DDD.

https://github.com/VaughnVernon/IDDD_Samples_NET/tree/master/iddd_identityaccess/Domain.Model/Identity

Sylvain Lecoy
  • 947
  • 6
  • 15
  • Can you explain why? Are you working on a very complex project? – w0051977 Jan 04 '18 at 09:44
  • When I first tried to separate domain objects from persistence objects I had a lot of trouble writing Translators classes (or convertors) for back and forth translating between layers. The project I worked recently in on Java, and Hibernate can map entities through XML mapping. I know its not sexy but at least it respect the domain object by not invading with lot of annotations. I am also in favor of having "pure" domain objects e.g. not polluted by persistence information, that's why I like the hexagonal architecture by using XML mapping to map my domain objects. – Sylvain Lecoy Jan 04 '18 at 09:47
  • When I mean trouble I mean extra work, extra tests to write, more maintenance for no added business value at all. – Sylvain Lecoy Jan 04 '18 at 09:48
  • The more complexe your project is, the more work it will be to maintain two objects for the same concept, one with great business value, the other with zero business value and only technical, plus the translators methods or factories. This will be LOT of more work in my opinion. – Sylvain Lecoy Jan 04 '18 at 09:50
  • There is nothing wrong persisting your domain object. Actually its very beautiful, think of it like this. new keyword create a new object that never existed before in the repository. The only way to create a new one is to instanciate one and then save it or persist it. To fetch an existing domain object, you ask the repository to recreate one, you never use the new keyword and you let the repo do this work for you. – Sylvain Lecoy Jan 04 '18 at 09:52
  • @SylvainLecoy: Agreed but configuring complex domain objects with NHibernate is difficult and in some cases may be error prone. There is no problem with mapping simple domain objects though. – Amit Joshi Jan 04 '18 at 10:03
  • I see, you mean the sample provided by Vaughn Vernon in C# is not feature complete ? E.g. no mappings ? On the java sample the application is functionnal and Hibernate mapping is not that much difficult I found. Might be another story for NHibernate, I tend to not use anymore ORM these day... – Sylvain Lecoy Jan 04 '18 at 12:32
  • 1
    I don't agree that "having separate classes would require more work because there is more code and tests to write". Writing code is not slow, complexity is not solely measured in number of lines of code. What slows down development and maintenance is bad code design where you end up thousands of lines of code in a single class having multiple responsibilities and dependencies. This will ultimately make it harder to understand, test and alter the code. – Adrian Hristov Jan 04 '18 at 14:04
  • 1
    @AdrianHristov: Agreed and that actually leads to the other problem mentioned in question. More you move closer to DDD, more difficult it is to utilize ORM features. I personally think it's a tread-off. One have to choose one over other and try to balance the both depending on each use-case in hand. – Amit Joshi Jan 05 '18 at 06:08
  • @SylvainLecoy: Hibernate and NHibernate stories are exactly the same. Mapping complex models (domain models) with any of it is difficult. I personally separate domain models from persistence models. – Amit Joshi Jan 05 '18 at 06:14
  • If the entity is too complexe for mapping. Maybe the issue is in the entity doing too much ? – Sylvain Lecoy Jan 15 '18 at 08:53