【问题标题】:Repository and Service Design question存储库和服务设计问题
【发布时间】:2010-02-22 18:31:11
【问题描述】:

我们正在尝试找出使用 DDD 时的最佳做法,并且我们正在讨论什么是最有意义的或“正确的方式”。

注意:所有代码均为伪代码。

承认以下几点:

public interface IDomainEntityAService
{
    void CreateMyObject(DomainEntityA myobject);
    DomainEntityA RetrieveDomainEntityA(long someId);
    //Other operations that handle the business logic dealing with MyObject
}

我们还有另一个服务,它使用 IDomainEntityAService 的一部分来满足特殊需求。

public interface IDomainEntityBService
{
    DomainEntityB GetDomainEntityB();
}

其中 OtherInformation 包含以下内容:

public class DomainEntityB
{
    public string Name { get; set; }
    public IList<DomainEntityA> DomainEntityAList { get; set; }
}

现在我们的问题来了。我们正在研究使用存储库来保存 OtherInformation,如下所示:

public interface IDomainEntityBRepository
{
    void Add(DomainEntityB information);
    DomainEntityB Get(long someId);
}

由于我们希望尽可能保持 DRY,因此理想情况下,我们希望重用 IDomainEntityAService 的逻辑来检索 DomainEntityB 的 DomainEntityAList 列表。哪一个最有意义?

A) 在 IDomainEntityBRepository 中有对 IDomainEntityAService 的引用 例如

public class SqlDomainEntityBRepository : IDomainEntityBRepository
{

    public SqlDomainEntityBRepository(IDomainEntityAService domainEntityAService, Database database)
    {

    }

    public void Add(DomainEntityB information)
    {
        //save DomainEntityB to SQL
    }
    public DomainEntityB Get(long someId)
    {
        //Get OtherInformation.Name from SQL
        //use domainEntityAService.Get() to populate the list of DomainEntityAList
        //return DomainEntityB
    }
}

B) IDomainEntityBRepository 仅处理 SQL 内容,我们使用 IHaveOtherInformation 的服务层来填充 MyObjects 列表

public class DomainEntityBService : IDomainEntityBService
{
    public DomainEntityBService(IDomainEntityAService domainEntityAService, IDomainEntityBRepository repo)
    {
    }
    public DomainEntityB GetDomainEntityB()
    {
        var domainEntityB = _repo.Get(someId);
        domainEntityB.DomainEntityAList = _domainEntityAService.GetAll(someId);
        return domainEntityB;
    }
}

C) 我们为 OtherInformation 创建一个特定的 DAL 对象,并使用服务层来组成 OtherInformation 的一个实例

public class DomainEntityBDAL
{
    public string Name { get; set; }
    public IList<int> DomainEntityAListIds { get; set; }
}

然后我们将有一个存储库来检索 OtherInformationDAL,然后代码将如下所示:

public class DomainEntityBService : IDomainEntityBService
{

    public DomainEntityBService(IDomainEntityAService domainEntityAService, IDomainEntityBRepository repo)
    {
    }
    public DomainEntityB GetDomainEntityB()
    {

        var domainEntityBDAL = _repo.Get(someId);
        DomainEntityB result = new DomainEntityB() { Name = domainEntityBDAL.Name };
        foreach (var id in domainEntityBDAL.DomainEntityAListIds)
        {
            result.DomainEntityAList.Add(_domainEntityAService.Get(id));
        }
        return result;
    }
}

D) 哇,我们完全不在基地,而是这样做!!!

我希望这是有道理的,并感谢您的帮助。

编辑备注:

也许英文描述可以帮助更好地描述我的问题。我们有 DomainEntityA 它是一个聚合根。有一个相应的服务来处理处理 DomainEntityA 的所有逻辑。

现在我们还有 DomainEntityB,它是一个聚合根。但是 DomainEntityB 有一个 DomainEntityAs 的列表。 DomainEntityAs 可以独立存在,但是 DomainEntityB 不能没有 DomainEntityAs 的列表

我们将如何加载 DomainEntityB 中的 DomainEntityA 项目列表并维护 DomainEntityA 的所有逻辑。

在 DomainEntityBService 中重用 DomainEntityAService?
在 DomainEntityBService 中创建一个单独的 DomainEntityARepository?

我们目前使用 EntLib,但正在寻找更多关于设计的信息,然后是 DAL 实现。

【问题讨论】:

  • 不应该直接回答你使用哪个 DAL 实现吗?你为什么对 Entlib 如此执着?
  • 如果某个 DAL 实现更容易实现这一点,我完全赞成。我们过去使用 Entlib,并计划将其与 Repository 模式一起使用。

标签: c# domain-driven-design service repository-pattern


【解决方案1】:

您如何改用一个通用接口来插入类型?像这样:

public interface IRepository<T>
{
    T Get(object id);
    void Save(T value);
    void Update(T value);
    void Delete(T value);
    IList<T> GetAll();
}

您使用的任何实现都应该能够读取类型并知道它如何适合数据库。简而言之,这就是存储库模式。如果您还没有完全了解 Entlib,请探索 NHibernate。我发现它很好地补充了存储库模式。我在我的博客上写了很多关于this topic 的文章,所以如果你想了解更多,可以去那里。

【讨论】:

  • 感谢您的信息,但请参阅我的编辑。如果 T 的子节点也是聚合根(在完全不同的域中),您将如何处理将这些项目加载到存储库中?
  • NHibernate 可以处理“T 的子代”(又名继承)。我不知道您所说的“在不同域中聚合根”可能是什么意思,但 NHibernate 几乎可以处理您能想到的任何关系。
  • 我不是说继承,“B”有一个“A”的列表。 “A”可以自己生存并被其他服务/应用程序使用。现在“B”需要一个“A”列表才能成为“B”的有效实例。假设要获取所有“A”(对于“B”的实例),它需要进行 Web 服务调用。那个电话应该在哪里直播?为“B”加载“A”的调用是否应该存在于 DAL、服务层或其他东西中?
  • B 应该只存储 A 标识符的列表(可能是整数?)。您应该为每个系统创建存储库实现,其中两个实现都继承 IRepository 接口。详细地说,您将为系统 A(使用 Web 服务)提供一个存储库,为系统 B(使用 NHibernate)提供一个存储库。您需要两个不同的存储库的原因是将两个系统解耦,这样当系统 A 更改时,您可以简单地创建其存储库的新版本,而不会干扰系统 B 的存储库。这在实践中效果很好。
【解决方案2】:

我在这里假设您将要存储到数据库中。

这个问题的答案很大程度上取决于您使用什么作为持久性引擎。如果您已经手动完成了自己的持久性,那么C 不是一个选项,因为您将看到 N+1 个查询。 B 很可能是我的第一选择,因为它提供了最多的代码重用,并希望其性能不会太差。

话虽如此,如果您使用 NHibernate 之类的东西,我建议您不要使用存储库模式,而是使用存储库模式

  • 将复杂查询封装为对象(然后可以对其进行单元测试)。
  • 直接使用会话

我希望这会有所帮助!

【讨论】:

  • 我们很可能会为我们的 DAL 使用 EntLib。
  • 您是否知道为什么 A 会被认为是坏的?存储库返回从多个位置收集的对象是不好的做法吗?例如要创建一个域模型的实例,我需要从 SQL、XML 和文件中查询?还是一个存储库应该只涉及一个数据存储?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多