是一个人的聚合根? [英] Is a Person an aggregate root?

查看:156
本文介绍了是一个人的聚合根?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于所有的实体都加盖单位是谁创造的/修改过的记录,我们可以考虑一个Person实体的聚合根的所有实体?

Since all entities have a stamped of who created/modified the record, can we consider a Person entity an aggregate root to all entities?

这是所有实体引用人将会成为一个集合的人,如:

That is, all entities that references the Person will become a collection to Person, e.g.

public class Person
{
    public virtual int PersonId { get; set; }
    public virtual string Lastname { get; set; }
    public virtual IList<OrderHeader> CreatedOrders { get; set; }
    public virtual IList<OrderHeader> ModifiedOrders { get; set; }

    // Other entities that have a reference on Person will be mapped as a collection under 
    // the Person entity
}


public class OrderHeader
{
    public virtual int OrderId { get; set; }        
    public virtual DateTime OrderDate { get; set; }
    public virtual Customer Customer { get; set; }
    public virtual string CommentsOnThisOrder { get; set; }

    // stamp audit-level concerns
    public virtual Person CreatedBy { get; set; }
    public virtual DateTime DateCreated { get; set; }

    public virtual Person ModifiedBy { get; set; }
    public virtual DateTime DateModified { get; set; }

    public virtual IList<OrderItem> OrderItems { get; set; }
}

public class OrderItem 
{
    public virtual OrderHeader OrderHeader { get; set; }
    public virtual Product Product { get; set; }
    public virtual int Quantity { get; set; }
    public virtual decimal Price { get; set; }
}

这将基本上让所有的实体成为集到的人,这违反了DDD聚合根规则。

That will basically make all entities become a collection to Person, which violates DDD aggregate root rules.

在我有限的DDD汇集起来的认识,OrderHeader绝不能成为一个集合的人,因为我们不应该通过人保存订单总和。唯一的 *入门* 点保存订单总量(对象图)必须从OrderHeader完成的,而不是从人。

In my limited understanding of DDD aggregrate, the OrderHeader must not become a collection to Person, as we should not save the Order aggregate via Person. The only *entry* point for saving the Order aggregate(object graph) must be done from the OrderHeader, not from the Person.

现在这才是我真正的目标,甚至为什么它看上去很脏,我仍然希望为了收集到的人:

Now here comes my real goal even why it looks dirty, I still wanted the Order to be a collection to Person:

还有一个ORM *咳嗽* NHibernate的*咳嗽*不了的做一个LEFT从Person JOIN(.DefaultIfEmpty),以OrderHeader如果OrderHeader没有映射为一个集合的人。实现从左边的人加盟到OrderHeader的唯一方法是将OrderHeader映射为一个集合的人。

There's one ORM *cough* NHibernate *cough* that cannot do a LEFT JOIN (.DefaultIfEmpty) from Person to OrderHeader if OrderHeader is not mapped as a collection to Person. The only way to achieve LEFT JOIN from Person to OrderHeader is to map the OrderHeader as a collection to Person.

我应该让基础设施问题(如的促进 LEFT通过的LINQ的.DefaultIfEmpty从人到OrderHeader JOIN通过使OrderHeader成为集对个人)打破何时应该只是一个实体必须成为一个聚合根规则?

Should I allow infrastructure concerns (e.g. facilitating LEFT JOIN from Person to OrderHeader via Linq's .DefaultIfEmpty by making the OrderHeader become a collection to Person) break the rules on when should only an entity must become an aggregate root?

如果我们不映射OrderHeader作为一个集合到人,那么如果我们需要做一个LEFT JOIN (扁平化的结果,不分层,因此需要使用LEFT JOIN)从人到OrderHeader NHibernate的,唯一的选择就是使用QueryOver。 QueryOver是非常繁琐相比,LINQ到写作。

If we will not map the OrderHeader as a collection to Person, then if we needed to make a LEFT JOIN (flattened result, not hierarchical, hence the need to use LEFT JOIN) from Person to OrderHeader on NHibernate, the only option left is to use QueryOver. QueryOver is very tedious to write compared to Linq.

无法在NHibernate的LINQ的使用.DefaultIfEmpty(LEFT JOIN功能),如果我们手工做一个LEFT JOIN(即通过LINQ的的加入和从人到OrderHeader .DefaultIfEmpty),.DefaultIfEmpty抛出,如果我们做一个手动LEFT JOIN NHibernate的,LEFT JOIN对NHibernate的LINQ的,必须通过收集和.DefaultIfEmpty做一个例外。

Cannot use the .DefaultIfEmpty(LEFT JOIN functionality) on NHibernate's Linq if we manually do a LEFT JOIN (i.e., via Linq's join and .DefaultIfEmpty) from Person to OrderHeader, .DefaultIfEmpty throws an exception if we do a manual LEFT JOIN on NHibernate, LEFT JOIN on NHibernate's Linq must be done via collection and .DefaultIfEmpty.

如果偶尔做(每个需求为基础),更是打破了规则上的聚合根务实的选择呢?例如,我应该在OrderHeader作为集合映射到人,以方便从左边的人JOIN通过LINQ到OrderHeader?

If done occasionally(per need-basis), is breaking the rules on aggregate root a pragmatic choice? Example, should I map the OrderHeader as collection to Person in order to facilitate LEFT JOIN from Person to OrderHeader via Linq?

修改

样品Northwind数据库。当我们需要他们的订单,包括那些客户没有订单(例如,巴黎)

Sample northwind database. When we need to report all customers with their orders including those customers without orders(e.g., PARIS)

 CustomerID     OrderID
 OTTIK      |   10407
 OTTIK      |   10684
 OTTIK      |   10554
 PARIS      |        
 PERIC      |   10502
 PERIC      |   10474
 PERIC      |   10995
 PERIC      |   10354
 PERIC      |   11073
 PERIC      |   10322



,我们需要做一个LEFT JOIN:

, we need to do a LEFT JOIN:

select c.CustomerID, o.OrderID
from customers c
left join orders o on c.CustomerID = o.CustomerID
order by c.CustomerID

这是可以做到的LINQ的加入,.DefaultIfEmpty()。不过NHibernate的LINQ的不能做人工加入的Linq的结果.DefaultIfEmpty,它抛出一个异常。 NHibernate的DefaultIfEmpty只能在一个集合被应用。但我觉得映射集合的东西,不是一个聚合根违反DDD聚合根规则。而且这样一来,所有的实体,如人(客户是另一个例子)将可能包含的所有的实体,每个表都有一个CreatedByPerson参考。

That can be done with Linq's join and .DefaultIfEmpty(). However NHibernate's Linq can't do a .DefaultIfEmpty on the result of manual Linq join, it throws an exception. NHibernate's DefaultIfEmpty can only be applied on a collection. But I feel mapping a collection to something that is not an aggregate root violates DDD aggregate root rules. And also doing so, all entities such as Person (Customer is another example) will potentially contain collections of ALL entities, as every table has a CreatedByPerson reference.

@DanielSchilling:

@DanielSchilling:

我也感到惊讶(一个好办法),即从引用OrderItem的OrderHeader违反了DDD。我们是否有某种白皮书或Martin Fowler的文章阐述上的?我想引用从子实体父实体不认为是基础设施的关注,因此被认为DDD。

I'm also surprised(in a good way), that referencing the OrderHeader from OrderItem violates DDD. Do we have some sort of whitepaper or a Martin Fowler article expounding on that? I thought referencing a parent entity from a child entity is not considered an infrastructure concern, thus considered DDD.

推荐答案

这听起来像你希望能够做到这一点:

Thoughts on DDD decisions...

It sounds like you want to be able to do this:

var people = session.Query<Person>()
    .FetchMany(x => x.CreatedOrders)
    .Where(x => x.Id == personId);

您应该只翻转查询各地,像这样......

You should just flip the query around, like so...

var orders = session.Query<OrderHeader>()
    .Fetch(x => x.CreatedBy)
    .Where(x => x.CreatedBy.Id == personId);

有不仅仅是DDD纯度更多的理由让 person.CreatedOrders 你的模型。如果你有一个人的地方,每天10单,一年中的每一天,一年又一年?第一个查询几乎肯定会加载更多的数据比实际需要。第二个查询,这并不需要一个 person.CreatedOrders 收集所有的人,让你更好地控制哪些订单获取多少获取。

There's more reasons than just DDD purity to keep person.CreatedOrders out of your model. What if you have one person that places 10 orders a day, every day of the year, year after year? The first query almost certainly loads more data than you actually need. The second query, the one that doesn't need a person.CreatedOrders collection at all, gives you more control over which orders to fetch and how many to fetch.

var orders = session.Query<OrderHeader>()
    .Fetch(x => x.CreatedBy)
    .Where(x => x.CreatedBy.Id == personId)
    .Where(x => x.OrderDate >= DateTime.Now.AddYears(-1))
    .Skip(100)
    .Take(50);



一般情况下,如果发现您违反一个DDD原则,它通常是一个很好的指标,你'再做错了什么,你通常不会有看远找到其他证据支持。

Generally, if you find you're violating a DDD principle, it's usually a good indicator that you're doing something wrong, and you usually don't have to look too far to find other supporting evidence.

每过一段时间,不过,你必须要弯曲规则了一下,我认为这是可以接受的。例如,我通常会尽量公正每个关系的一个侧面映射 - 这是我真正从DDD的角度需要。当引用其他聚合的根,这通常意味着只是造型多到一一侧,一个都汇集根中的引用 - 从根到其子,这通常意味着只是造型一对许多和离开多到一缩小模型。这将意味着离开 OrderItem.OrderHeader 缩小模型。

Every once in a while, though, you do have to bend the rules a bit, and I think that's acceptable. For example, I usually try to just map one side of each relationship - that's all I really need from a DDD perspective. When referencing other aggregate roots, that usually means just modeling the many-to-one side, and for references within an aggregrate root - from the root to its children, that usually means just modeling the one-to-many and leaving the many-to-one out of the model. That would mean leaving OrderItem.OrderHeader out of the model.

如果你需要找出多少一个特定的产品已经售出,但仅限于已提交的订单?要做到这一点,最简单的方法是,像这样:

What if you needed to find out how much of a particular product had been sold, but only for Orders that have been Submitted? The easiest way to do this would be like so:

var quantitySold = session.Query<OrderItem>()
    .Where(x => x.Product.Id == productId && x.OrderHeader.Submitted)
    .Sum(x => x.Quantity);



因此,在这种情况下,我们需要两个一对一模型许多 orderHeader.OrderItems ,并在多到一 orderItem.OrderHeader 。这是因为它是最有效的这笔款项直接访问相关的OrderItems,而不是先通过 OrderHeader 为DDD将决定。我与该确定。

So in this case, we need to model both the one-to-many orderHeader.OrderItems and also the many-to-one orderItem.OrderHeader. This is because it is most efficient for this SUM to access the relevant OrderItems directly, rather than first go through OrderHeader as DDD would dictate. I'm OK with that.

忽略的部分DDD一个拿到订单......

Ignoring the DDD part of the question, this LEFT OUTER JOIN can be simulated by combining the results of two separate queries - one to get the orders...

var orders = session.Query<OrderHeader>()
    .Fetch(x => x.CreatedBy);



...而另一个得到不发号施令的人:

... and another to get the people that have no orders:

var peopleWithNoOrders = session.Query<Person>()
    .Where(p => !session.Query<OrderHeader>().Any(o => o.CreatedBy == p));

这篇关于是一个人的聚合根?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆