DDD实体利用服务

我有一个应用程序,我正在尝试用至少一个名义上的DDD类型域模型构建,并且正在与某个部分进行斗争。

我的实体有一些业务逻辑,它使用我目前在某些域服务中的一些财务计算和速率计算,以及我在一个值对象中放置的一些常量值。

我正在努力解决如何让实体使用域服务中的逻辑,或者这些服务中的逻辑是否属于那里。 这是我到目前为止:

public class Ticket { public Ticket(int id, ConstantRates constantRates, FinancialCalculationService f, RateCalculationService r) { Id = id; ConstantRates = constantRates; FinancialCalculator = f; RateCalculator = r; } private FinancialCalculationService FinancialCalculator { get; set; } private RateCalculationService RateCalculator { get; set; } private ConstantRates ConstantRates { get; set; } public int Id { get; private set; } public double ProjectedCosts { get; set; } public double ProjectedBenefits { get; set; } public double CalculateFinancialGain() { var discountRate = RateCalculator.CalculateDiscountRate(ConstantRates.Rate1, ConstantRates.Rate2, ConstantRates.Rate3); return FinancialCalculator.CalculateNetPresentValue(discountRate, new[] {ProjectedCosts*-1, ProjectedBenefits}); } } public class ConstantRates { public double Rate1 { get; set; } public double Rate2 { get; set; } public double Rate3 { get; set; } } public class RateCalculationService { public double CalculateDiscountRate(double rate1, double rate2, double rate3 ) { //do some jibba jabba return 8.0; } } public class FinancialCalculationService { public double CalculateNetPresentValue(double rate, params double[] values) { return Microsoft.VisualBasic.Financial.NPV(rate, ref values); } } 

我觉得某些计算逻辑确实属于那些域服务,但不是真的喜欢我必须从我的存储库手动注入这些依赖项。 是否有另一种方式可以建模? 我不喜欢那个吗?

读过蓝皮书但之前没有真正构建过这种风格的东西,我正在寻找指导。

编辑

谢谢大家的反馈! 基于我所听到的,听起来我的模型看起来应该更像下面的内容。 这看起来更好?

 public class Ticket { public Ticket(int id) { Id = id; } private ConstantRates ConstantRates { get; set; } public int Id { get; private set; } public double ProjectedCosts { get; set; } public double ProjectedBenefits { get; set; } public double FinancialGain { get; set; } } public class ConstantRates { public double Rate1 { get; set; } public double Rate2 { get; set; } public double Rate3 { get; set; } } public class FinancialGainCalculationService { public FinancialGainCalculationService(RateCalculationService rateCalculator, FinancialCalculationService financialCalculator, ConstantRateFactory rateFactory) { RateCalculator = rateCalculator; FinancialCalculator = financialCalculator; RateFactory = rateFactory; } private RateCalculationService RateCalculator { get; set; } private FinancialCalculationService FinancialCalculator { get; set; } private ConstantRateFactory RateFactory { get; set; } public void CalculateFinancialGainFor(Ticket ticket) { var constantRates = RateFactory.Create(); var discountRate = RateCalculator.CalculateDiscountRate(constantRates.Rate1, constantRates.Rate2, constantRates.Rate3); ticket.FinancialGain = FinancialCalculator.CalculateNetPresentValue(discountRate, new[] {ticket.ProjectedCosts*-1, ticket.ProjectedBenefits}); } } public class ConstantRateFactory { public ConstantRates Create() { return new ConstantRates(); } } public class RateCalculationService { public double CalculateDiscountRate(double rate1, double rate2, double rate3 ) { //do some jibba jabba return 8.0; } } public class FinancialCalculationService { public double CalculateNetPresentValue(double rate, params double[] values) { return Microsoft.VisualBasic.Financial.NPV(rate, ref values); } } 

域模型在这一点上最终变得相当贫乏,但是当我添加function时,它可能会有更多function。

编辑2

好的,我得到了更多的反馈,也许我的“计算”服务更像是战略对象,我的实体可以依赖它。 这是对实体的更多逻辑的另一种看法,并利用这些策略对象。 对此的想法? 直接在实体中实例化这些助手的任何问题? 我认为我不想在我的测试中嘲笑那些,但OTOH我也无法在不测试这些策略对象的情况下测试CalculateFinancialGain方法。

 public class Ticket { public Ticket(int id, ConstantRates constantRates) { Id = id; ConstantRates = constantRates; } private ConstantRates ConstantRates { get; set; } public int Id { get; private set; } public double ProjectedCosts { get; set; } public double ProjectedBenefits { get; set; } public double CalculateFinancialGain() { var rateCalculator = new RateCalculator(); var financeCalculator = new FinanceCalculator(); var discountRate = rateCalculator.CalculateDiscountRate(ConstantRates.Rate1, ConstantRates.Rate2, ConstantRates.Rate3); return financeCalculator.CalculateNetPresentValue(discountRate, ProjectedCosts*-1, ProjectedBenefits); } } public class ConstantRates { public double Rate1 { get; set; } public double Rate2 { get; set; } public double Rate3 { get; set; } } public class RateCalculator { public double CalculateDiscountRate(double rate1, double rate2, double rate3 ) { //do some jibba jabba return 8.0; } } public class FinanceCalculator { public double CalculateNetPresentValue(double rate, params double[] values) { return Microsoft.VisualBasic.Financial.NPV(rate, ref values); } } 

让您的服务接受Ticket实体作为参数。 服务应该是无状态的,同一服务应该能够为任意数量的实体提供服务。

在您的情况下,我会将FinancialCalculatorServiceRateCalculatorService拉出您的实体,并使每个服务上的方法接受Ticket实体作为参数。

花点时间阅读pg。 Eric Evans的领域驱动设计 105

鉴于我们在课程中看到的内容,我认为它们不是真正的蓝皮书 服务 ,我会将计算器保留在Ticket

FinancialCalculatorServiceRateCalculationService都不依赖于域实体 – 它们都对原始值进行操作。 应用程序不必担心如何计算票证可能带来的经济收益,因此将这些信息封装在票证本身中是很有价值的。

如果他们真的不依赖于域实体,请考虑将它们视为“独立类”​​而不是“服务”(再次,在蓝皮书术语中)。 对于Ticket依赖于策略对象( FinancialCalculatorRateCalculator )肯定是合适的,这些对象本身不具有外来依赖性并且本身不会修改域实体的状态。

编辑2的更新 。 我认为使计算器分离类的一个优点是,您可以独立于Ticket测试它们。 严格来说,门票不负责执行这些计算,他们负责对这些合作课程进行正确的调用。 所以我倾向于让它们像你在最初的例子中那样注入/模拟。

我会说服务使用实体,而不是相反。

另一件事,在您的域名上不确定,但您确定票证是实体而不是价值对象吗?

你实际上已经提出了一个问题,即已经进行了相当多的讨论。 在曲目的两边都有信徒,所以你需要自己决定最有意义的东西。

就个人而言,我没有我的实体使用服务,因为它围绕“如何干净地将服务提供给我的实体?”创建了大量工作。 题。

在我看来,像CalculateFinancialGains()更像是一个服务级别的调用。 这确实导致Ticket非常贫血,但我认为它有其他行为? 如果不是那可能是一种气味……

这个问题实际上是“清洁代码”一书中讨论的一个例子(第96-97页)。 根本问题是是否使用程序方法或面向对象方法。 希望我不违反重复这里的几个部分,但这是鲍勃·马丁所说的指导:

过程代码(使用数据结构的代码)使得在不改变现有数据结构的情况下添加新函数变得容易。 另一方面,OO代码使得在不改变现有function的情况下添加新类变得容易。

赞美也是如此:

程序代码使得添加新数据结构变得困难,因为所有function都必须改变。 OO代码使得添加新函数变得很困难,因为所有类都必须更改。

我理解DDD“值类型”将是Bob Martin称之为数据结构的东西。

希望这会有所帮助,而不只是增加噪音:)