DDD工厂责任

时间:2016-03-04 00:48:06

标签: domain-driven-design factory-pattern ddd-repositories

如果有以下代码。

public class CountryFactory : IEntityFactory
{
    private readonly IRepository<Country> countryRepository;

    public CountryFactory(IRepository<Country> countryRepository)
    {
        this.countryRepository = countryRepository;
    }

    public Country CreateCountry(string name)
    {
        if (countryRepository.FindAll().Any(c => c.Name == name))
        {
            throw new ArgumentException("There is already a country with that name!");
        }

        return new Country(name);
    }
}

从DDD方法来看,是创建Country的正确方法。或者更好的是CountryService检查一个国家是否存在,如果不存在,则只需要调用工厂返回一个新实体。这将意味着服务将负责持久化实体而不是工厂。

我对责任所在的位置感到困惑。特别是如果需要创建更复杂的实体,这不像创建国家那么简单。

2 个答案:

答案 0 :(得分:1)

在DDD中,工厂用于封装复杂对象和聚合创建。通常,工厂不是作为单独的类实现,而是在聚合根类上实现返回新聚合的静态方法。

工厂方法比构造函数更适合,因为您可能需要使用技术构造函数来进行序列化,而var x = new Country(name)在您的泛在语言中几乎没有意义。这是什么意思?为什么在创建国家时需要名称?您是否真的创建了国家/地区,新国家出现的频率,甚至您是否需要对此流程进行建模?如果你开始考虑你的模型和除了战术模式之外的无处不在的语言,所有这些问题都会出现。

工厂必须返回有效对象(即聚合),检查其中的所有不变量,但不检查外部。工厂可能会收到服务和存储库作为参数,但这也不常见。通常,您有一个应用程序服务或命令处理程序来执行某些验证,然后使用工厂方法创建新聚合并将其添加到存储库。

Lev Gorodinski在Factory Pattern where should this live in DDD?

也有一个很好的答案

此外,Red Book的第11章广泛描述了工厂的实施。

答案 1 :(得分:1)

将存储库注入工厂是可以的,但它不应该是您的第一个问题。起点应该是:您的业务领域需要什么样的一致性?

通过检查属于您的域图层的CountryFactory中的国家/地区名称唯一性,您会给自己留下这样的印象:这些国家/地区始终保持一致。但唯一的聚合是Country,并且由于没有AllCountries聚合作为一致性边界,因此无法保证对此不变量的尊重。有人总是潜入一个新的国家,与你正在添加的国家名称完全相同,就在你检查之后。您可以做的是将CreateCountry操作包装到一个事务中,该事务将锁定整个国家/地区(如果您使用RDBMS,则锁定整个表),但这会损害并发性。

还有其他选择需要考虑。

  • 为什么不利用数据库唯一约束来强制执行国家/地区名称不变量?作为补充,您还可以在UI级别使用另一个检查点来警告用户他们输入的国家/地区名称已被占用。这将需要另一个&#34;查询&#34;只调用CountryRepository.GetByName()的服务,但不希望修改返回的国家/地区。

    很快你就会意识到有两种模型 - 可以在给定的时刻给你一些域数据,以便你可以在用户界面上显示它们,以及那些暴露操作的模型(AddCountry)并保证域不变量始终保持不变。这是向CQRS迈出的第一步。

  • 添加或修改国家/地区的频率是多少?如果它很高,我们真的需要一个国家名称在任何时候都是唯一的吗?如果我们放松了约束并允许用户临时创建一个重复的国家名称,它不会解决很多问题吗?机制可以稍后检测重复项并采取补偿操作,将新添加的国家置于保持状态并联系用户以要求他们更改名称。 A.k.a最终的一致性,而不是立即的一致性。

  • Country是否需要成为聚合?如果它是一个价值对象并且在每个使用它的实体中重复会有什么代价呢?