当您在“我如何加载 1000 个订单项中的一个?”中说加载时你的意思是“从数据库加载”?换句话说,我如何从数据库中只加载聚合根的一个子实体?
这有点复杂,但您可以让存储库返回聚合根的派生,其字段是延迟加载的。例如
namespace Domain
{
public class LineItem
{
public int Id { get; set; }
// stuff
}
public class Order
{
public int Id { get; set; }
protected ReadOnlyCollection<LineItem> LineItemsField;
public ReadOnlyCollection<LineItem> LineItems { get; protected set; }
}
public interface IOrderRepository
{
Order Get(int id);
}
}
namespace Repositories
{
// Concrete order repository
public class OrderRepository : IOrderRepository
{
public Order Get(int id)
{
Func<IEnumerable<LineItem>> getAllFunc = () =>
{
Collection<LineItem> coll;
// { logic to build all objects from database }
return coll;
};
Func<int, LineItem> getSingleFunc = idParam =>
{
LineItem ent;
// { logic to build object with 'id' from database }
return ent;
};
// ** return internal lazy-loading derived type **
return new LazyLoadedOrder(getAllFunc, getSingleFunc);
}
}
// lazy-loading internal derivative of Order, that sets LineItemsField
// to a ReadOnlyCollection constructed with a lazy-loading list.
internal class LazyLoadedOrder : Order
{
public LazyLoadedOrder(
Func<IEnumerable<LineItem>> getAllFunc,
Func<int, LineItem> getSingleFunc)
{
LineItemsField =
new ReadOnlyCollection<LineItem>(
new LazyLoadedReadOnlyLineItemList(getAllFunc, getSingleFunc));
}
}
// lazy-loading backing store for LazyLoadedOrder.LineItems
internal class LazyLoadedReadOnlyLineItemList : IList<LineItem>
{
private readonly Func<IEnumerable<LineItem>> _getAllFunc;
private readonly Func<int, LineItem> _getSingleFunc;
public LazyLoadedReadOnlyLineItemList(
Func<IEnumerable<LineItem>> getAllFunc,
Func<int, LineItem> getSingleFunc)
{
_getAllFunc = getAllFunc;
_getSingleFunc = getSingleFunc;
}
private List<LineItem> _backingStore;
private List<LineItem> GetBackingStore()
{
if (_backingStore == null)
_backingStore = _getAllFunc().ToList(); // ** lazy-load all **
return _backingStore;
}
public LineItem this[int index]
{
get
{
if (_backingStore == null) // bypass GetBackingStore
return _getSingleFunc(index); // ** lazy-load only one from DB **
return _backingStore[index];
}
set { throw new NotSupportedException(); }
}
// "getter" implementations that use lazy-loading
public IEnumerator<LineItem> GetEnumerator() { return GetBackingStore().GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public bool Contains(LineItem item) { return GetBackingStore().Contains(item); }
public void CopyTo(LineItem[] array, int arrayIndex) { GetBackingStore().CopyTo(array, arrayIndex); }
public int Count { get { return GetBackingStore().Count; } }
public bool IsReadOnly { get { return true; } }
public int IndexOf(LineItem item) { return GetBackingStore().IndexOf(item); }
// "setter" implementations are not supported on readonly collection
public void Add(LineItem item) { throw new NotSupportedException("Read-Only"); }
public void Clear() { throw new NotSupportedException("Read-Only"); }
public bool Remove(LineItem item) { throw new NotSupportedException("Read-Only"); }
public void Insert(int index, LineItem item) { throw new NotSupportedException("Read-Only"); }
public void RemoveAt(int index) { throw new NotSupportedException("Read-Only"); }
}
}
OrderRepository.Get(int) 的调用者会收到实际上只是一个 Order 对象的东西,但实际上是一个 LazyLoadedOrder。当然,要做到这一点,您的聚合根必须提供一两个虚拟成员,并围绕这些扩展点进行设计。
编辑以解决问题更新
在地址的情况下,我会将其视为值对象,即数据的不可变组合一起被视为单个值。
public class Address
{
public Address(string street, string city)
{
Street = street;
City = city;
}
public string Street {get; private set;}
public string City {get; private set;}
}
然后,为了修改聚合,您创建了一个新的 Address 实例。这类似于 DateTime 的行为。您还可以向 Address 添加方法方法,例如 SetStreet(string),但这些应该返回 Address 的新实例,就像 DateTime 的方法返回 DateTime 的新实例一样。
在您的情况下,不可变的 Address 值对象必须与 Addresses 集合的某种观察相结合。一种简单明了的技术是在单独的集合中跟踪添加和删除的 AddressValues。
public class Customer
{
public IEnumerable<Address> Addresses { get; private set; }
// backed by Collection<Address>
public IEnumerable<Address> AddedAddresses { get; private set; }
// backed by Collection<Address>
public IEnumerable<Address> RemovedAddresses { get; private set; }
public void AddAddress(Address address)
{
// validation, security, etc
AddedAddresses.Add(address);
}
public void RemoveAddress(Address address)
{
// validation, security, etc
RemovedAddresses.Add(address);
}
// call this to "update" an address
public void Replace(Address remove, Address add)
{
RemovedAddresses.Add(remove);
AddedAddresses.Add(add);
}
}
或者,您可以使用ObservableCollection<Address> 支持地址。
这确实是一个纯粹的 DDD 解决方案,但你提到了 NHibernate。我不是 NHibernate 专家,但我想您必须添加一些代码才能让 NHibernate 知道地址更改的存储位置。