Java 重构访问遗留系统中存储库的域逻辑

Java 重构访问遗留系统中存储库的域逻辑,java,refactoring,repository,domain-driven-design,Java,Refactoring,Repository,Domain Driven Design,我正在使用一个旧系统,该系统有一个贫血的域模型 域具有以下实体类别:Car,CarType,CarComponent,CarComponentType 对于其中的每一个,都有一个单独的存储库。还有许多服务访问这些存储库,基本上包含所有逻辑 我需要实现一种方法,确定供应商是否可以停止CarComponentType。逻辑如下:只有在目前没有配备该部件的现有汽车的情况下,才能停止使用该部件 最初,我在一个服务类中实现了这一点 public boolean canBeDiscontinued(CarC

我正在使用一个旧系统,该系统有一个贫血的域模型

域具有以下实体类别:
Car
CarType
CarComponent
CarComponentType

对于其中的每一个,都有一个单独的存储库。还有许多服务访问这些存储库,基本上包含所有逻辑

我需要实现一种方法,确定供应商是否可以停止
CarComponentType
。逻辑如下:只有在目前没有配备该部件的现有汽车的情况下,才能停止使用该部件

最初,我在一个服务类中实现了这一点

public boolean canBeDiscontinued(CarComponentType carComponentType) {
    List<Car> cars = carRepository.getCarsWithComponent(carComponentType);
    return cars.isEmpty();
}
但是,我不能把它放在那里,因为它需要访问存储库(据我所知,对于实体来说,了解数据访问层是非常严重的反模式)。加载组件类型时,我无法加载该类型的所有车辆,因为这可能是数千个对象。我们没有使用任何ORM,因此为其创建一个延迟加载的集合不仅庞大而且非常容易出错

像我第一次做的那样,在服务类中实际使用这个方法更合适吗?这不重要吗?还有其他选择吗?我应该从另一个起点开始重构吗


还有一个类似的问题。但我的问题与Java有关,所以我认为该解决方案不适用于我的情况。另外,对于使用汽车和组件作为我的域模型,请提前表示歉意。:)

这听起来像是弗雷德里克·盖瑟斯(Frederik Gheyses)的一个很好的候选答案,虽然可能有点短。详细说明:在您的例子中,您可以首先为您的规范定义一个接口(请原谅我的C#语法):

然后,您可以创建使用您的存储库的ICarSpecification实现。大概是这样的:

public class CanDiscontinueSpecification : ICarSpecification
{
    private readonly CarRepository carRepository;

    public CanDiscontinueSpecification(CarRepository carRepository)
    {
         this.carRepository = carRepository;
    }

    public bool IsSatisfiedBy(CarComponentType carComponentType)
    {
        return this.carRepository.GetCarsWithComponent(carComponentType).Any();
    }
}
你可以到此为止,但我不太喜欢规范模式的一点是它不是很容易被发现。一种可能的解决方案是将规范注入CarComponentType本身:

public class CarComponentType
{
    private readonly ICarSpecification discontinueSpec;

    public CarComponentType(ICarSpecification discontinueSpec)
    {
        this.discontinueSpec = discontinueSpec;
    }

    public bool CanBeDiscontinued()
    {
        return this.discontinueSpec.IsSatisfiedBy(this);
    }
}
或者,如果您不想在类的每个实例中都使用规范,可以使用方法注入而不是构造函数注入:

public bool CanBeDiscontinued(ICarSpecification spec)
{
    return spec.IsSatisfiedBy(this);
}

这种方法在实现方面并没有真正增加任何价值,但更容易被发现。

我不认为说我和下一个人一样讨厌贫血的领域模型是一种轻描淡写的说法

然而,考虑到您正在工作的系统已经建立了贫血领域模型/服务(anti)模式,我认为引入其他模式可以在孤立情况下阻止系统,我相信您在这里已经做到了。这样的决定应该由团队做出,并且应该有明确的好处

另外,IMHO,我认为您的原始代码片段比其他答案中提出的解决方案更简单(没有冒犯Mark和Frederik,只是一个观察:-),而且更复杂的解决方案实际上没有带来任何好处-我的意思是,这两种情况下的功能是相同的,但后者使用了更多的移动部件

就如何进行而言,仅举一个例子很难说。引入一个ORM(您提到的,目前还没有使用)可能是一种方法,因为它会减少您服务类中的代码,并且会有明显的好处,所以我很想从这里开始,然后一旦它到位,再回顾一下情况。

我不认为“这可以被中断”属于这些类中的任何一个。谁负责确定一个零件是否可以停产?不是汽车或汽车部件。我认为您在服务中实现的初始方法是正确的。也许您需要一个CarInventoryManagementService,负责回答有关汽车库存项目的问题,如:

carsUsingComponent( CarComponent comp )
canComponentBeDiscontinued( CarComponent comp )
etc

如果代码中有多个地方需要询问与库存相关的问题,例如“canBeDiscontinued”,那么创建一个具有该职责的服务可能是有意义的。

谢谢。我自己也几乎接受了这个答案,但出于学术目的,我想知道。我觉得应该有一个更聪明的解决方案,我只是没有看到。谢谢你的澄清。如果我有更多的规格,可以结合起来,这似乎是适当的。就我而言,我不确定。我没有向每个实体中注入存储库,而是注入了使用存储库的规范。这真的好得多吗?好吧,它将类与存储库分离,通过传递满足测试要求的规范实现,可能更容易进行单元测试。是的:chriskes刚才说的:)说真的,这使得规范只是另一种策略,可能依赖于存储库,也可能不依赖于存储库。尽管如此,我还是加入了方法注入的替代方案,以防你真的不喜欢对规范有一个通用的依赖性。我看到了解耦的好处,但如果我到处使用它,它看起来就像是“又一个无用的层反模式”。@waxwing:不要把我的回答当作是试图说服你做一件事或另一件事。我的意思只是详细说明你可能采取的方向。就个人而言,我喜欢将我的域实体保留为POCOs/POJO,因此将规范注入CarComponentType不是我的首选解决方案。作为替代方案,您可以简单地在类上拥有一个布尔属性,并让一个工厂(了解规范)正确设置它。这有点主观。域对象是否应该“知道”客户端如何使用它,这通常是一个临界情况。也许你在这件事上是对的。但假设它是一个不同的函数——毫无疑问,它适合CarComponentType,但需要对象本身不包含的数据。这是我更感兴趣的。我在考虑责任驱动的设计
public bool CanBeDiscontinued(ICarSpecification spec)
{
    return spec.IsSatisfiedBy(this);
}
carsUsingComponent( CarComponent comp )
canComponentBeDiscontinued( CarComponent comp )
etc