分离域模型和持久性模型时,更改跟踪如何成为问题?

时间:2018-01-03 15:53:34

标签: c# nhibernate domain-driven-design

我在这里阅读这个问题:Having Separate Domain Model and Persistence Model in DDD

并专门查看此代码:

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

此代码表示存在单独的域模型和持久性模型。我试图了解使用这种方法的局限性。在整个互联网上,我读到使用NHibernate时变更跟踪是一个问题,但是我不明白为什么。在将域模型映射回持久性模型之后处理更改跟踪。如何跟踪变更问题?如何改变跟踪是一个问题的实际例子将有助于我。

更新 请参阅以下代码:

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

我在应用程序服务中执行此操作:

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

Q1)此代码中ORM的好处是否已丢失,例如改变追踪?请注意,持久性对象从存储库返回,然后映射到域对象,然后返回到同一个持久性对象。

Q2)NHibernate如何跟踪更改,即如何知道Ticket(持久性对象)是数据库中的故障单1。我想这不仅仅是身份证。

3 个答案:

答案 0 :(得分:2)

更改跟踪不成问题。域与持久性的混合是。 &#39;域名模型&#39;主要是一些容易映射到表的数据结构。在许多域中,您可能会将99%的数据结构与一些规则相关联。在这些情况下,您的域模型看起来与持久性模型非常相似。

但是,在更抽象的层面上,领域模型主要模拟商业行为,状态(数据)只是一个工件。此外,它从业务角度(功能)来看待事物。

持久性模型是关于存储状态,即以易于检索的方式构建的数据。对于具有复杂功能且涉及许多概念及其特定模型和特定于用例的业务规则的域,结果模型与持久状态完全不同。

ORM如何跟踪更改只是一个与DDD无关的实现细节,但是如果域足够丰富,那么简单的CRUD解决方案,尤其是域模型的思维模式 = 国家+行为 aka类成为障碍。有或没有ORM。

对于Domain = 98%Persistence Models的应用程序,没有问题,你可以使用你想要的任何ORM。

答案 1 :(得分:1)

@MikeSW回答的大部分细节都是正确的;我只是不同意变更跟踪。我的答案更多的是NHibernate而不是DDD。

是的,更改跟踪将是一个问题,但这取决于ISession的管理方式。此外,不仅改变跟踪,它还将影响NHibernate的其他功能,如会话级缓存,延迟加载等。

我们假设ISession是在请求级别上管理的,即每个请求一个ISession。下面提到的所有活动都是一个单一请求的一部分。

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

现在,以下是同一请求中应用程序中的代码:

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

NHibernate能够跟踪Ticket中发生的变化,但这种能力在这里没用。从Ticket返回时GetTicket丢失,Ticket创建新的SaveTicket。尽管ISession处于请求级别并且能够看到更改发生,但根本不使用NHibernate的更改跟踪功能。

以下代码(绕过域模型)将正确跟踪更改:

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

以下是获取和修改Ticket的方法:

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

现在,你不打电话给SaveTicket;相反,您Flush在应用程序中某处检测到EndOfRequest的ISession

在这种情况下,NHibernate的更改跟踪会跟踪对Ticket所做的更改并自动刷新这些更改。

通过持久性模型转换为域模型,我们绕过了NHibernate的这种能力,因为持久性模型永远不会改变。

每种方法都有其优点和缺点。请参阅this问题。

修改(适用于更新

Q1):如果相同的持久性模型实例被修改并且对同一ISession可见,则新代码将从更改跟踪中受益。在这种情况下,它还将受益于会话级缓存。在使用AutoMapper进行映射时,如果可能不需要任何实体,NHibernate将加载引用的实体。这取决于每个用例。

Q2):实际上这应该是一个不同的问题,在这个答案中太宽泛了。无论如何,请参考this

答案 2 :(得分:1)

另外,我不建议将域模型类与持久性对象分开。

你可能想看看Vaughn Vernon在DOT.NET上的应用程序做了什么

他是着名的实施领域驱动设计(IDDD)一书的作者。必须阅读我建议任何认真对待DDD的开发人员。

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