对于新应用程序,我正在使用图层:
RestRessource - > ApplicationService - >域
我有一个基本用例,用户可以创建一个“bduget”。如果当前年份没有其他预算,则状态为“初始”。在可以创建新预算并将其状态“纠正”与另一个属性“索引”相关联之后:纠正#1,纠正#2,......
现在有些编码。 ApplicationService有一个名为“createBudget”的方法
@Autowired
private BudgetRepository budgetRepo;
@Autowired
private BudgetService budgetService;
public Budget createBudget(int year) {
Budget newBudget;
if (budgetService.existsBudgetInitialFor(year)) {
newBudget = new Budget(year, "INITIAL");
} else {
newBudget = new Budget(year, "CORRECTIVE", budgetService.nextIndex());
}
budgetRepo.insert(newBudget);
return newBudget;
}
BudgetService正在使用BudgetRepository来计算数据库。 existsBudgetInitialFor返回一个布尔值,如果count> 0:)
现在的问题是:
budgetService.existsBudgetInitialFor(year)
作为私有方法提取,模拟它以我想要的方式返回TRUE / FALSE等。createInitialBudget
命令然后发送createCorrectiveBudget
命令,会更好吗?在这种情况下,我们需要检查命令的有效性(以避免多次初始创建:单一验证)。这只是一个非常简单的代码(我放弃了权限管理,验证等)。所以我尝试在编写更复杂的用例之前制作这种代码。主要目的是验证图层,向同事解释,表明可以测试此代码等。
非常感谢!
弗朗索瓦
答案 0 :(得分:0)
您的应用程序服务似乎有业务逻辑。如果是这种情况,它属于域服务。然后应用程序服务就像你现在一样调用domainBudgetService.CreateBudget(int year)。如果需要,可以将existsBudgetInitialFor移动到存储库。
参见#1
是的工厂是首选。
如果您将预算服务方法移至存储库,那么我不认为这是一个问题。我通常只对我的域名服务和实体进行单元测试。
工厂应该消除这种尴尬
我只需要一个createBudget命令,并在内部保留逻辑就像你拥有它一样。否则,UI现在需要具有业务逻辑来确定要发送的命令类型。
希望这有帮助。
答案 1 :(得分:0)
这是一项实施问题,属于预算本身及其有关如何命名某种状态的知识:
a)域名服务:
numberOfBudgets = budgetRepository.numberOfBudgetsIn(anYear);
budget = Budget.newFrom(anYear, numberOfBudgets + 1);
budgetRepository.add(budget);
b)预算类中的公共工厂方法:
Budget.newFrom(anYear, budgetNumber) {
status = 'INITIAL';
if (numberOfBudgets > 0) {
status = 'CORRECTIVE' + budgetNumber
}
return new Budget(anYear, status);
}
这样,您只能在a点中提到一个域名服务。
干杯, 塞巴斯蒂安。
答案 2 :(得分:0)
对于这个例子,我发现budgetServcie没用。搬家更好吗? existsBudgetInitialFor在BudgetRepository内部?
好主意我说,我个人不喜欢xxxService(xxx是实体名称),名称本身并没有多大意义。
你认为“if ...... else ......”的声明是在好的地方:是的 它对AppService层负责或对你来说,它是一个域规则和 要强制执行此操作,您是否在BudgetService中移动此代码?
在这个用例中,我认为将语句留在应用程序服务中是可以接受的。由于单元测试仍然很简单,有两条备用路径(初始和预算存在)。如果用例变得更复杂或者其他一些用例涉及预算创建,那么您可能需要一个BudgetFactory来保存该语句。
要封装budgetService.nextIndex调用,最好起诉a 工厂创建预算? (代替构造函数)。并做 你认为现在是计算nextIndex的最佳时机。
是的,我认为现在是时候了。如果没有在这里计算,它很可能会在持久层中计算,这更难以测试,因为它们通常需要更多的努力来编写和维护,而且更脆弱。
应用程序层是subjet到单元测试或者可能只是 整合测试?为什么要问这个?当我试图进行单元测试时 这个方法我需要至少模拟BudgetService, BudgetRepository,我在想:它是代码味道的标志吗? 另一种简化单元测试的解决方案是提取 budgetService.existsBudgetInitialFor(year)作为私有方法,mock 它按我的意愿返回TRUE / FALSE等。
考虑到此用例的简单性,如果将if-else语句留在应用程序层中,请进行单元测试。如果引入BudgetFactory来封装if-else和nextIndex,则应用程序层将变得很薄。如果你采用tdd,你仍然可以编写单元测试,如果没有,也可以通过功能测试保留它。
我没有想到在编写此代码时创建的是 实体的责任。我找到了“如果......别的话”的尴尬 ......“预算实体内部的逻辑。我是对的吗?(请告诉我,我是 至少一次:D) 我同意。单个预算无法判断是否存在其他预算。
我问自己的最后一个问题:也许我错了 开始。如果UI发送createInitialBudget命令并且更好 然后是createCorrectiveBudget命令?在这种情况下,我们需要检查 命令有效性(避免多次初始创建:独特 验证)。
当UI显示带有createCorrectiveBudget和createInitialBudget的两个按钮并让用户选择他/她应该单击的按钮时,我认为这不是一个好主意。后端的验证很好,但知识泄漏,它迫使人们做出不必要的选择。