C# 在域驱动设计中,将调用放在其他对象上是否违反DDD';域对象中的报告?
我目前正在一个即将结束的项目中重构一些代码,最后我将许多业务逻辑放入服务类中,而不是域对象中。此时,大多数域对象只是数据容器。我决定在服务对象中编写大部分业务逻辑,然后将所有内容重构为更好、更可重用和更可读的形状。这样,我就可以决定哪些代码应该放在域对象中,哪些代码应该拆分成它们自己的新对象,哪些代码应该留在服务类中。所以我有一些代码:C# 在域驱动设计中,将调用放在其他对象上是否违反DDD';域对象中的报告?,c#,design-patterns,service,domain-driven-design,C#,Design Patterns,Service,Domain Driven Design,我目前正在一个即将结束的项目中重构一些代码,最后我将许多业务逻辑放入服务类中,而不是域对象中。此时,大多数域对象只是数据容器。我决定在服务对象中编写大部分业务逻辑,然后将所有内容重构为更好、更可重用和更可读的形状。这样,我就可以决定哪些代码应该放在域对象中,哪些代码应该拆分成它们自己的新对象,哪些代码应该留在服务类中。所以我有一些代码: public decimal CaculateBatchTotal(VendorApplicationBatch batch) { IList<
public decimal CaculateBatchTotal(VendorApplicationBatch batch)
{
IList<VendorApplication> applications = AppRepo.GetByBatchId(batch.Id);
if (applications == null || applications.Count == 0)
throw new ArgumentException("There were no applications for this batch, that shouldn't be possible");
decimal total = 0m;
foreach (VendorApplication app in applications)
total += app.Amount;
return total;
}
public decimal CaculateBatchTotal(供应商应用程序批处理)
{
IList applications=AppRepo.GetByBatchId(batch.Id);
if(applications==null | | applications.Count==0)
抛出新ArgumentException(“此批处理没有应用程序,这不可能”);
小数总数=0米;
foreach(应用程序中的供应商应用程序应用程序)
合计+=应用金额;
返回总数;
}
这段代码似乎可以很好地添加到域对象中,因为它的唯一输入参数是域对象本身。似乎是进行重构的最佳人选。但唯一的问题是这个对象调用另一个对象的存储库。这让我想把它留在服务课上
因此,我的问题是:
编辑注3:我不认为批处理对象应该能够合计任何应用程序列表,它似乎只能合计该特定批处理中的应用程序。否则,将函数保留在服务类中对我来说更有意义。为什么不传入一个IList
作为参数,而不是VendorApplicationBatch?这方面的调用代码可能来自一个可以访问AppRepo的服务。这样一来,您的存储库访问权限将上升到它所属的位置,而您的域功能仍然可以对数据来自何处一无所知。我不是DDD方面的专家,但我记得伟大的杰里米·米勒(Jeremy Miller)的一篇文章为我回答了这个问题。您通常需要与域对象相关的逻辑—在这些对象内部,但您的服务类将执行包含此逻辑的方法。这帮助我将特定于域的逻辑推送到实体类中,并使我的服务类不那么庞大(正如我发现自己在服务类中加入了很多逻辑,如您所提到的)
编辑:示例
我使用企业库进行简单验证,因此在entity类中我将设置如下属性:
[StringLengthValidator(1, 100)]
public string Username {
get { return mUsername; }
set { mUsername = value; }
}
实体继承自基类,该基类具有以下“IsValid”方法,该方法将确保每个对象满足验证标准
public bool IsValid()
{
mResults = new ValidationResults();
Validate(mResults);
return mResults.IsValid();
}
[SelfValidation()]
public virtual void Validate(ValidationResults results)
{
if (!object.ReferenceEquals(this.GetType(), typeof(BusinessBase<T>))) {
Validator validator = ValidationFactory.CreateValidator(this.GetType());
results.AddAllResults(validator.Validate(this));
}
//before we return the bool value, if we have any validation results map them into the
//broken rules property so the parent class can display them to the end user
if (!results.IsValid()) {
mBrokenRules = new List<BrokenRule>();
foreach (Microsoft.Practices.EnterpriseLibrary.Validation.ValidationResult result in results) {
mRule = new BrokenRule();
mRule.Message = result.Message;
mRule.PropertyName = result.Key.ToString();
mBrokenRules.Add(mRule);
}
}
}
一个更复杂的例子可能是银行账户。存款逻辑将存在于account对象中,但服务类将调用此方法。据我所知(没有足够的信息知道这是否是正确的设计),VendorApplicationBatch应在域对象中包含一个延迟加载的IList,并且逻辑应保留在域中
例如(航空代码):
公共类供应商应用程序批处理{
私有IList应用程序{get;set;};
公共十进制CaculateBatchTotal()
{
if(Applications==null | | Applications.Count==0)
抛出新ArgumentException(“此批处理没有应用程序,这不可能”);
小数总数=0米;
foreach(应用程序中的供应商应用程序应用程序)
合计+=应用金额;
返回总数;
}
}
使用像NHibernate这样的ORM很容易做到这一点,我认为这将是最好的解决方案。您甚至不应该从域对象访问存储库 您可以做的是让服务为域对象提供适当的信息,或者在由服务或构造函数设置的域对象中有一个委托
public DomainObject(delegate getApplicationsByBatchID)
{
...
}
在我看来,CalculateTotal是一个针对VendorApplication集合的服务,返回批次的VendorApplication集合自然符合Batch类的属性。因此,其他一些服务/控制器/任何东西将从批中检索VendorApplication的适当集合,并将它们传递给VendorApplicationTotalCalculator服务(或类似的服务)。但这可能会破坏某些DDD聚合根服务规则或我不知道的类似规则(DDD新手)。如果我将汇总一个批的代码放在存储库中,而不是getbybatchid,则无法保证传入的应用程序将来自该批。我觉得batch应该只能合计它包含的应用程序。totals a batch in the repository->totals a batch in the batch domain object我不确定我是否理解。我的建议是保持代码的原样,但不要在应用程序列表中传递VendorApplicationBatch。GetByBatchID仍在存储库中,总计的代码仍在域中。public decimal CalculateBatchTotal(IList应用程序)我正在尝试决定是否应将此函数放入VendorBatchApplication对象中。似乎一个批处理只能处理所有应用程序
public class VendorApplicationBatch {
private IList<VendorApplication> Applications {get; set;};
public decimal CaculateBatchTotal()
{
if (Applications == null || Applications.Count == 0)
throw new ArgumentException("There were no applications for this batch, that shouldn't be possible");
decimal Total = 0m;
foreach (VendorApplication App in Applications)
Total += App.Amount;
return Total;
}
}
public DomainObject(delegate getApplicationsByBatchID)
{
...
}