在DDD中,当我从存储库中获取实体时,如何为Id属性添加水合物?当我第一次创建实体时(在持久性之前),我可以在实体的构造函数中生成唯一的ID,但是当我从存储库中获取项目时,我已经有一个ID!那我该如何设置呢?将ID传递给实体的构造函数只是感觉不正确,或者也许是吧?
我没有使用ORM。
public interface IPersonRepository
{
Person GetById(long id);
}
public abstract class Entity
{
public long Id { get; private set; }
protected Entity()
{
Id = // Generate a unique Id with some algorithm.
}
}
public sealed class Person : Entity
{
//...
}
当我第一次创建实体时(在持久性之前),我可以在实体的构造函数中生成唯一的ID ...
这可能不是一个好主意。非确定性数据(例如时间或远程可变状态的副本)应输入到域模型中。在实践中,您经常会逃脱它。但这本身并不能使它成为一个好主意。
通常的答案是,存储库将获取信息的持久表示形式(例如,DTO),并将其交给目的是构建实体的factory。
因此,实体的身份只是从存储库传递到工厂的另一条信息。
现在,“工厂”只是另一个生命周期模式;它可以采用许多不同的形式,包括构造函数的形式。在这种情况下,标识符通常只是作为参数传递给实体。
尤其是标识符可能有点怪异,因为它们通常不表达业务语义。标识符模式的典型特征是它们实际上是不透明的,仅支持相等比较。您的实体几乎永远不会查看自己的标识符来弄清楚下一步该怎么做。
但是如果您的实体需要引用其自身的标识符,则无论出于何种原因,通常在初始化对象时都会创建该引用,并且从此以后一直保持不变(换句话说,实体标识符属性是一个对不可变值的不可变引用)。
1)聚合还是实体?我认为您在DDD方面的问题有些困惑。通常,您不应该加载实体。您应该通过聚合根(实体)来加载聚合,该聚合的所有其他实体都应自动加载。
来自Evans DDD:
仅AGGREGATE根可以直接通过数据库查询获得。所有其他对象必须通过关联遍历找到。
聚合是数据存储传输的基本元素-您请求加载或保存整个聚合。
2)如何设置ID。]使用不可变属性是个好主意。 public long Id { get; private set; }
,让我们以为使用不可变ID时我们做的正确。现在,让我们继续并找到正确设置ID的可能方法。
构造函数的ID。
公共构造函数必须遵循与工厂相同的规则:它必须是满足所创建对象所有不变量的原子操作。
工厂。>>来自Evans DDD:
清晰简单。我会选择这个。我将ID和其他数据存储在一起(it's common practise)。特别是AGGREGATES的复杂程序集要求工厂
反序列化过程中的设置ID。
GetById(long id);
返回在反序列化期间已经设置了Person
的Id
。