域驱动设计:每个聚合根的存储库?

时间:2011-02-10 21:30:11

标签: domain-driven-design repository

我正在试图弄清楚如何完成以下任务:

User can have many Websites

在向用户添加新网站之前我需要做的是获取网站URL并将其传递给方法,该方法将检查网站是否已存在于数据库中(另一个用户具有相同的网站关联),或是否创建新记录。 < =原因是是创建新缩略图还是使用现有缩略图。

问题是存储库应该是每个聚合根,这意味着我不能做我上面解释的内容? - 我可以首先获取数据库中的所有用户,然后使用if语句查看用户具有相同URL的网站记录的位置,但这将导致无休止且缓慢的过程。

3 个答案:

答案 0 :(得分:3)

无论您使用何种存储库方法,您都应该能够以某种方式指定标准。因此,搜索与相关网站相关联的用户 - 如果搜索没有返回用户,则该网站未被使用。

例如,您可以添加具有以下签名的方法(或者将查询对象作为described in this article传递):

User GetUser(string hasUrl);

该方法应该或多或少地生成SQL:

select u.userId
from   User u
join   Website w
on     w.UserId = u.UserId
where  w.Url    = @url

这应该与直接查询Website表几乎一样有效;没有必要将所有用户和网站记录加载到内存中。让您的关系数据库完成繁重工作,让您的存储库实现(或对象关系映射器)处理转换。

答案 1 :(得分:1)

我认为您的模型存在根本问题。如果我理解正确,网站是用户聚合组的一部分。这意味着网站实例没有全局范围,仅在属于用户的上下文中才有意义。

但是现在当用户想要添加新网站时,首先要在创建新网站之前检查“网站是否存在于数据库中”。这意味着网站实际上确实具有全球范围。否则,只要用户请求新网站,您就可以为该特定用户创建一个新网站,该网站在该用户范围内具有意义。在这里,您拥有共享的网站,因此在许多用户的范围内具有意义,因此不属于用户聚合。

修复您的模型,您将解决查询困难。

答案 2 :(得分:0)

一种策略是实现可以验证约束的服务。

public interface IWebsiteUniquenessValidator 
{
    bool IsWebsiteUnique(string websiteUrl);
}

然后你必须实现它,你如何做到这将取决于我不知道的因素,但我建议不要担心通过域。简单一点,它只是一个查询(* - 我会在底部添加这个)。

public class WebsiteUniquenessValidator : IWebsiteUniquenessValidator
{
 //.....
}

然后,将“注入”到需要它的方法中。我说“注入”因为我们将从域外提供给域对象,但是..我们将使用方法参数而不是构造函数参数(为了避免要求我们的实体由我们的Io​​C容器实例化) )。

public class User 
{
    public void AddWebsite(string websiteUrl, IWebsiteUniquenessValidator uniquenessValidator) 
    {
        if (!uniquenessValidator.IsWebsiteUnique(websiteUrl) {
            throw new ValidationException(...);
        }

        //....
    }
}

无论用户及其存储库的用户是什么 - 如果是Service类或CommandHandler - 都可以提供唯一性验证器依赖性。这个消费者应该已经通过IoC接线,因为它将占用UserRepository:

public class UserService 
{
    private readonly IUserRepository _repo;
    private readonly IWebsiteUniquenessValidator _validator;

    public UserService(IUserRepository repo, IWebsiteUniquenessValidator validator) 
    {
        _repo = repo;
        _validator = validator;
    }

    public Result AddWebsiteToUser(Guid userId, string websiteUrl)
    {
        try {
            var user = _repo.Get(userId);
            user.AddWebsite(websiteUrl, _validator);
        }
        catch (AggregateNotFoundException ex) {
          //....
        }
        catch (ValidationException ex) {
          //....
        }
    } 


}

*我提到简化验证并避开域。

我们构建域来封装修改数据时经常出现的复杂行为。

经验表明,围绕变更数据的要求与查询数据的要求非常不同。

这似乎是您遇到的一个痛点,因为您试图强制读取通过写入系统。

为了减轻这些痛点,可以从写入端分离域中数据的读取。

CQRS是此技术的名称。我只想说,一旦我在CQRS的背景下查看DDD,就会发现一大堆灯泡被点击。我强烈建议您尝试理解CQRS的概念。