DDD - 从实体访问存储库,bis

时间:2011-09-02 23:51:11

标签: orm domain-driven-design entity repository-pattern ddd-repositories

上个月我问过一个previous question关于一个实体是否应该访问一个存储库,虽然看起来大多数人认为他们不应该这样做,但我不得不承认我很难被说服。

我有一个用例,我实际上无法想到任何(合理的)方法来执行逻辑而不在我的实体中注入存储库:

我们有Store,分配了Zone(城市,区,... - 用户定义)。 为了减少负责将Stores添加到数据库的员工的工作量,并确保一致性,我们不要求他选择他想要添加商店的区域。他只是放大地图,点击以查明商店位置,然后保存。然后,应用程序必须找到此位置最相关的区域。

我目前拥有的是:

class Store
{
    protected Zone zone;
    protected Point location;
    protected ZoneRepository zoneRepository;

    public void setLocation(Point location)
    {
        Zone matchingZone = this.zoneRepository.findByLocation(location);
        if (matchingZone == null) {
            throw new ApplicationException(
                "The store location must be within a zone boundary");
        }
        this.location = location;
        this.zone = matchingZone;
    }
}

您是否有任何 solid 替代方案可以支持这种设计本质上不好的普遍接受的观点?

4 个答案:

答案 0 :(得分:3)

这并不一定说这个设计很糟糕。它只是提出了问题。大多数情况下,不需要在实体内使用存储库。让商店根据地理位置查找自己的区域似乎有点奇怪。看起来这个责任不属于商店。

总是需要点吗?如果用户想要从“最后5个区域”列表中选择区域,该怎么办?还是来自国家/城市的萧条?或者从搜索文本框?在这些情况下没有必要指出。需要点和存储库似乎将域模型定制为UI模型。

如果确实每个商店都需要一个点,那么您可能不需要同时使用Zone字段。如果您仅在需要时动态查找Store的区域,该怎么办?使用最新的Point-to-Zone数据库。

此外,如果您的Store类有5个或6个方法,并且只有一个方法需要zoneRepository字段,那么该类不是cohesive

答案 1 :(得分:1)

就逐点搜索的DDD而言,你应该为它提供单独的服务,例如LocationService,它将把你的存储库封装在里面。此服务应该对User商店实体一无所知,特别是永远不要将存储库包含在实体中。

答案 2 :(得分:1)

根据您提供的说明,似乎Point实际上不是您的无所不在的语言的一部分 - 区域是。您没有为商店设置点 - 您为其分配区域。换句话说,此操作的签名不应为setLocation(Point location) - 它应为assignZone(Zone locationZone)。用户选择的点与区域之间的转换应在执行域模型操作之前进行。这就是我的模型。

评论后添加的示例(C#,如果你不介意 - 并只显示一个概念)。代码假定基于命令的方法来执行用户操作 - 这就是我倾向于这样做的方式,但可能是Application ServiceController偶数,具体取决于应用程序的结构。

    public class StoreLocationHandler : Handles<LocateStoreCommand>
    {
        public void Handle(LocationStoreCommand command)
        {
            // Location contains coordinates as well as zone info and can be obtained only via LocationService
            Location location = LocationService.IdentifyZone(command.Coordinates); 

            Store store = StoreRepository.Get(command.StoreId);

            store.AssignLocation(location);

            // persist changes - either by Unit of Work or Repository
        }
    }

关键在于您不必在域实体中创建所有内容 - 从Store的角度来看,区域似乎是值对象。此外,进一步分离这两个概念可能会带来更多可能性,例如识别区域不在线但通过某种后台进程(为了性能或可伸缩性)。此外,在我看来,考虑到Dependency Injection原则,它更适合。

毕竟,域并不关心如何创建或检索区域,它关心Store,它的位置和它所属的区域之间的关联。为什么要对点和区域之间的转换负责?至少那是我看到它的方式。

答案 3 :(得分:1)

我使用相同的方法和你一样,无法确信它是一个糟糕的设计。但是,我有我的存储库的接口,所以我不使用ZoneRepository的具体实现,因为它可能变得很难,并且根据上下文不可能测试和模拟它。

另一点,正如@Samich所说的那样,根据Store对象中的一个点来获取区域的知识听起来不错,因为以后你可能想在另一个地方使用这个方法。如果您为区域提供服务,那么您将集中代码并避免冗余。