C# 使用服务的DDD实体
我有一个应用程序,我正在尝试用至少一个名义上的DDD类型的域模型来构建,并且正在与某个特定的部分进行斗争 我的实体有一些业务逻辑,它使用我目前在一些域服务中使用的一些财务计算和利率计算,以及我在值对象中输入的一些常量值 我正在努力让实体使用域服务中的逻辑,或者这些服务中的逻辑是否属于域服务。这就是我到目前为止所做的:C# 使用服务的DDD实体,c#,domain-driven-design,C#,Domain Driven Design,我有一个应用程序,我正在尝试用至少一个名义上的DDD类型的域模型来构建,并且正在与某个特定的部分进行斗争 我的实体有一些业务逻辑,它使用我目前在一些域服务中使用的一些财务计算和利率计算,以及我在值对象中输入的一些常量值 我正在努力让实体使用域服务中的逻辑,或者这些服务中的逻辑是否属于域服务。这就是我到目前为止所做的: public class Ticket { public Ticket(int id, ConstantRates constantRates, FinancialCalc
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
好的,我得到了更多的反馈,也许我的“计算”服务更像是我的实体可以依赖的策略对象。这里是另一个关于实体中更多逻辑的观点,并利用这些策略对象。对此有何想法?直接在实体中实例化这些助手有什么问题吗?我不想在我的测试中模仿这些,但是如果不测试这些策略对象,我也无法测试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);
}
}
我会说服务使用实体,而不是相反
另一件事,在您的域中不确定,但您确定票证是一个实体而不是一个值对象吗?您实际上已经想到了一个已经有相当多讨论的问题。这两条轨道的两边都有信徒,所以你需要自己决定什么才是最有意义的 就我个人而言,我没有让我的实体使用服务,因为它围绕着“如何将服务干净地导入实体?”的问题创建了大量的工作
在我看来,CalculateFinancialGains()更像是一个服务级别调用。这确实会导致票非常贫血,但我认为它有其他行为?如果没有,那可能是一种气味…让您的服务接受
票据实体作为参数。服务应该是无状态的,并且同一个服务应该能够向任意数量的实体提供其服务
在您的情况下,我会将FinancialCalculator服务
和RateCalculator服务
从您的实体中拉出,并使每个服务上的方法接受票据
实体作为参数
让我们看一看这个问题实际上是《清洁代码》(第96-97页)一书中讨论的一个例子。根本的问题是是否使用过程方法或面向对象方法。希望我没有在这里重复几个部分,但以下是Bob Martin的指导说明:
过程代码(使用数据结构的代码)可以轻松地添加新函数,而无需更改现有的数据结构。另一方面,OO代码可以轻松地添加新类,而无需更改现有函数
这种赞美也是正确的:
由于所有函数都必须更改,因此过程代码很难添加新的数据结构。OO代码很难添加新函数,因为所有类都必须更改
我的理解是,DDD“值类型”就是Bob Martin所说的数据结构
希望这会有所帮助,而不仅仅是增加噪音:)鉴于我们所看到的课程,我认为它们不是真正的服务,我会把计算器放在票证中
无论是FinancialCalculatorService
还是RateCalculationService
都不依赖于域实体-它们都基于原始值进行操作。应用程序不必担心如何计算票证带来的财务收益,因此将这些信息封装在票证中是很有价值的
如果他们真的没有依赖于域实体,考虑把它们看作“独立类”而不是“服务”(再一次,在蓝皮书术语中)。这当然适用于
Ticket
依赖于策略对象(FinancialCalculator
和RateCalculator
),这些对象本身不具有外来依赖项,也不修改域实体的状态
编辑2的更新。我认为将计算器分成不同的类的好处之一是,您可以独立于票证
对它们进行测试。严格地说,票证不负责执行这些计算,它们负责对协作类进行正确的调用。因此,我倾向于让它们像在您最初的示例中一样可注入/模拟。您能描述一下“Ticket”与域的关系吗?我认为Ticket是我这里的聚合根。它基本上是一个应用程序的增强/缺陷请求。应用程序正试图根据预计成本/收益计算特定维护票证的“价值”。您是否使用IoC模式?我建议将这些计算器移动到可以注入的属性。将使测试负载更容易。是的,我正在使用IoC容器。现在我想起来了,我可以很容易地将这些注入我的实体。我也将我的存储库用作工厂,但我想我现在需要将它们分开。据我所知,如果我使用ORM