我读过这个https://stackoverflow.com/questions/5818898/where-to-put-global-rules-validation-in-ddd,并创建了我的验证器。
但是现在我需要创建一个验证器,它必须检查订单实体。
这就是问题所在:当我创建或编辑实体"Car“时,我需要检查两个条件:
如果有10辆或更多的实体车,我不能拥有带有值“红色”的属性颜色=“红色”。如果实体"Warehouse“中没有带有"FF0000”项代码的条目,则属性颜色不能包含值" Red“--我需要使用ICarRepository来计数属性红色,使用IWarehouseRepostiory来检查是否有代码"FF0000”的项。
public class CarValidator : IValidator<Car>
{
public CarValidator(ICarRepository carRepository, IWahrehouseRepository wahrehouseRepository)
{
.....
}
private readonly IList<ISpecification<Car>> Rules =
new List<ISpecification<Car>>
{
new AvaiableRedColorSpecification(carRepository, wahrehouseRepository),
};
public bool IsValid(Car entity)
{
return BrokenRules(entity).Count() > 0;
}
public IEnumerable<string> BrokenRules(Car entity)
{
return Rules.Where(rule => !rule.IsSatisfiedBy(entity))
.Select(rule => GetMessageForBrokenRule(rule));
}
}
public class AvaiableRedColorSpecification : ISpecification<Car>
{
public bool IsSatisfiedBy(ICarRepository carRepository, IWahrehouseRepository wahrehouseRepository)
{
return carRepository.CountRedColor() < 10 &&
wahrehouseRepository.HaveItemCode("FF0000");
}
}
注入我的依赖的最佳方法是什么,因为通过这种方式,我需要在Car实体中为Add/Edit注入存储库,我认为在实体中注入存储库不是一个好主意
我已经决定使用验证模式,所以我创建了一个
interface IRuleSpecification<T>
{
Task<ValidatorResult> IsSatisfiedAsync(T subject);
}
interface IValidator<T, TResult>
{
Task ExecuteCheckAsync(T entity, TResult validateObject);
bool IsValid { get; }
IEnumerable<ValidatorResult> BrokenRules { get; }
TResult ValidateObject { get; }
}
public static async Task<IValidator<CarDto, CarEntity>> FactoryCreator(CarDto dto, IEnumerable<IRuleSpecification<CarDto>> rules)
{ ... }
在这种情况下,每当我尝试创建实体汽车时,我都必须在具有调用工厂的响应性的类中传递IRuleSpecification (在我的例子中,使用我称为CarCreatorCommand的CQRS )。
CarCreatorCommand通过依赖注入提高IRuleSpecification列表。
IRuleSpecification的实现类已经注入了IRepository以重新表示颜色的列表。这样,验证器就有了IReposiory的依赖性。
发布于 2020-10-13 09:02:27
当您将存储库或其他依赖项注入域时,域变得不那么纯净了。不纯域模型很难进行测试和推理。因此,纯域模型是首选。
那么,在不注入存储库的情况下,如何才能满足以下两个条件呢?
如果有10辆或更多的实体车,我不能拥有带有值“红色”的属性颜色=“红色”。如果实体"Warehouse“没有带有项目代码"FF0000”的条目,则属性颜色不能具有值"Red“。
Car
实体可以有一个将所需信息作为参数的方法或构造函数,而不是从存储库中获取它。例如:
public static Car Create(Model model, Engine engine, Color desiredColor, IReadOnlyList<ColorCount> usedColors, IReadOnlyList<Color> availableColors)
{
// do validation on usedColors and availableColors
return new Car(model, engine, desiredColor);
}
在从应用程序服务调用此方法之前,从存储库中获取信息。usedColors集合包含使用每种颜色的次数,availableColors集合包含仓库中的所有颜色。
这样,所有决策都可以在域中进行,而不需要任何进程外的依赖关系.
发布于 2020-10-14 19:30:45
这里少了点什么。实际的规则是什么?
当然,显而易见的答案是,您可以为任何Car
创建一个Color
。你就是不能用它做任何事!你想用你的Car
干什么?也就是说,这个系统的行为是什么?
我们是在尝试Assemble
他们吗?然后呢?Store
?
class CarFactory
{
public CarFactory(List<Model> models, List<Engine> engines, List<Color> colors)
public Car Assemble(CarSpec spec)
{
//validate specification against available components
}
}
// VO
class CarSpec
{
public readonly Model model
public readonly Engine engine
public readonly Color color
}
class CarWarehouse
{
public CarWarehouse(List<Car> cars)
// alternatively
public CarWarehouse(Dictionary<Color, int> numberOfEachColor)
public InventoryTicket Store(Car car)
{
//validate the Car's current color against inventory
}
}
现在我们可以推测出这样的用例:
// Manufacture Car Handler
var spec = new CarSpec(cmd.Model, cmd.Engine, cmd.Color)
var car = factory.Assemble(spec) // may throw
var ticket = warehouse.Store(car) // may throw
repo.Save(car, ticket)
重要的是,上面提到的不是数据,而是我们系统的行为。我们并不担心通用的“规范”或“规则”,也不担心是否有任何“有效”。我们只是在嵌入域逻辑..。好吧..。直接进入域名!这样更简单。每个不变量都是在其应用的相同上下文中强制执行的。也就是说,在与其相关的行为单位内。
我们不关心Car
的组件,直到我们想要对Car
做些什么。这就是业务流程的工作方式:“如果Car
不符合某些标准,我们就无法制造(组装和存储)它。”DDD寻求提供的基本视角(实际上是OOP)是,行为需要与数据放在一起,而不是围绕数据。
这样,Validator<Car>
就真正地代表了DDD的对立面。我们选择关注数据(Car
)而不是行为。我们不仅仅是在验证一个Car
,我们还在某些上下文中验证一个Car
。这是什么背景?我看不出你领域里有这些话。有东西不见了..。
发布于 2020-10-13 12:13:11
显然,领域有一个“规则”的概念。这些规则超越了Car实体,但是定义了什么是有效的仓库。
假设需求会增加,并且解决方案应该支持多个仓库。在这种情况下,每个仓库应该支持一套不同的规则(取决于仓库的大小、文化问题、.)。
因此,这些规则的验证与仓库的addCar
函数相关联。汽车不应该为其他汽车而烦恼。实体不应该为它们所处的集合而烦恼。
您所面临的问题是该设计缺陷的直接后果。
我可以想象用例应该限制仓库中汽车的可用颜色的数量;如果是这样的话,规则应该是仓库的一个可访问属性:wareHouse.availableCarColors
。
https://softwareengineering.stackexchange.com/questions/417857
复制相似问题