我有一个应用程序,我正在尝试用至少一个名义上的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);
}
}
此时,域模型最终变得相当贫乏,但是当我添加功能时,它可能会有更多功能。
编辑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);
}
}
答案 0 :(得分:6)
让您的服务接受Ticket
实体作为参数。服务应该是无状态的,同一服务应该能够为任意数量的实体提供服务。
在您的情况下,我会将FinancialCalculatorService
和RateCalculatorService
拉出您的实体,并使每个服务上的方法接受Ticket
实体作为参数。
请等一下,阅读pg. 105
的Domain-Driven Design by Eric Evans答案 1 :(得分:3)
鉴于我们在课程中看到的内容,我认为它们不是 blue book sense中的服务,我会将计算器保留在Ticket
中
FinancialCalculatorService
或RateCalculationService
都不依赖于域实体 - 它们都对原始值进行操作。应用程序不应该担心如何计算票证可能带来的经济收益,因此将这些信息封装在票证本身中是很有价值的。
如果他们真的不依赖于域实体,请考虑将它们视为“独立类”而不是“服务”(再次,在蓝皮书术语中)。对于Ticket
来说,依赖于策略对象(FinancialCalculator
和RateCalculator
)肯定是合适的,这些对象本身不具有外来依赖关系并且本身不会修改域实体的状态。
编辑2的更新。我认为使计算器分开类的优点之一就是你可以独立于Ticket
测试它们。严格来说,门票不负责执行这些计算,他们负责对这些合作课程进行正确的调用。所以我倾向于让它们像你在最初的例子中一样注入/模拟。
答案 2 :(得分:2)
我会说服务使用实体,而不是相反。
另一件事,在您的域名上不确定,但您确定票证是实体而不是价值对象吗?
答案 3 :(得分:2)
你实际上已经提出了一个问题,即已经进行了相当多的讨论。在曲目的两边都有信徒所以你需要自己决定什么是最有意义的。
就个人而言,我没有我的实体使用服务,因为它创造了大量的工作围绕“如何干净地将服务提供给我的实体?”问题
在我看来,像CalculateFinancialGains()更像是一个服务级别的调用。这确实导致Ticket非常贫血,但我认为它有其他行为?如果它不是那可能是一种气味......
答案 4 :(得分:1)
这个问题实际上是“清洁代码”一书(第96-97页)中讨论的一个例子。根本问题是是否使用程序方法或面向对象方法。希望我没有违反这里重复的几个部分,但这是鲍勃·马丁所说的指导:
程序代码(使用数据结构的代码)可以轻松添加新功能而无需更改现有数据结构。另一方面,OO代码可以在不改变现有功能的情况下轻松添加新类。
赞美也是如此:
程序代码使得添加新数据结构变得困难,因为所有功能都必须改变。 OO代码使得添加新函数变得很困难,因为所有类都必须更改。
我理解DDD“值类型”将是Bob Martin称之为数据结构的东西。
希望这会有所帮助,而不只是增加噪音:)