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

How is Change Tracking an issue when separating the Domain Model and Persistence Model?

我在这里读到这个问题: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();
    }
}

这段代码意味着有一个单独的域模型和持久性模型。我试图了解使用这种方法的局限性。在整个 Internet 上,我读到有关使用 NHibernate 时更改跟踪是一个问题的信息,但我不明白为什么。 Change Tracking 在领域模型映射回持久化模型之后处理。更改跟踪如何成为一个问题?一个关于变更跟踪如何成为一个问题的实际例子会对我有所帮助。

更新 请看下面的代码:

//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 如何跟踪更改,即它如何知道票证(持久对象)是数据库中的票证 1。我想这不仅仅是ID。

更改跟踪没问题。域与持久性的混合是。 'domain model'主要是一些数据结构,很容易映射到table。在许多领域中,您可能会处理 99% 的附加规则数据结构。在这些情况下,您的域模型看起来与持久性模型几乎相同。

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

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

ORM 如何跟踪变化只是一个实现细节,与 DDD 无关,但是如果领域足够丰富,简单的 CRUD 解决方案,尤其是 思维方式 domain model = state+behaviour a.k.a 类 成为障碍。有或没有 ORM。

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

@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 中发生的更改,但该功能在这里没有用。从 GetTicket 返回时 Ticket 丢失,而 SaveTicket 时创建新的 Ticket。 NHibernate 的更改跟踪功能根本没有使用,即使 ISession 处于请求级别并且能够看到发生的更改。

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

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

以下是如何获取和修改Ticket:

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

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

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

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

每种方法各有利弊。参考 this 问题。

编辑:(为您的更新

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

Q2:实际上这应该是一个不同的问题,因为太宽泛而无法在此答案中回答。无论如何,参考 this.

附带说明一下,我不建议将域模型 类 与持久性对象分开。

您可能想看看 Vaughn Vernon 在 DOT.NET

上为他的申请做了什么

他是著名著作《实施领域驱动设计》(IDDD) 的作者。必读,我推荐给所有认真对待 DDD 的开发人员。

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