我最近花时间阅读 SOLID 原则,并决定看看我使用的代码库如何比较。
在我们的一些代码中有一个存储库(存储库 A)。当要从存储库 A 中删除一条记录时,我们还需要从存储库 B 中删除关联的记录。因此,原始编码器创建了对存储库 B 的具体实现的依赖关系。存储库 A 中的方法位于事务内,并且从存储库 A 中删除记录,然后调用存储库 B 上的方法删除关联数据。
我对S原则的理解是每个对象应该只有1个改变的理由,但是对于我的存储库A来说有2个改变的理由?还是我离目标太远了?
存储库应该具有单一职责 - 持久保存一种实体。例如。雇员。如果您必须从其他存储库中删除一些关联的记录,它看起来就像业务逻辑。例如。
当员工被解雇时我们应该删除他的工作日志
业务逻辑通常发生在领域服务中。该服务将拥有两个存储库并完成所有工作:
staffService.Fire(employee)
实施看起来像这样
public class StaffService
{
private IEmployeeRepository employeeRepository;
private IWorkLogRepository workLogRepository;
private IUnitOfWorkFactory uowFactory;
// inject dependencies
public void Fire(Employee employee)
{
using(var uow = uowFactory.SartNew())
{
workLogRepository.DeleteByEmployee(employee.Id);
employeeRepository.Delete(employee.Id);
uow.Commit();
}
}
}
所以,基本建议
您可能想知道如果您有员工并且它有一些存储在不同数据库表中的嵌套对象,该怎么办。如果您与员工分开使用该对象,那么一切都如上所述 - 您应该有单独的存储库,以及操作两个存储库的其他对象(服务)。但是,如果您不与员工分开使用该嵌套对象,那么员工是一个聚合根,您应该只有员工存储库,它将查询内部的两个表。
(虽然这是一个老问题,但我认为这个答案可能是对@Sergey答案的补充)
对于@jonathan提到的情况,我认为我们可以使用一个工厂(也许是他用来创建objA的同一个工厂)来负责获取objB,如下所示:
class RepositoryA {
private final Factory factory;
public RepositoryA(Factory factory) {
this.factory = factory;
}
public ClassA GetById(string id) {
ClassB objB = factory.GetObjBById(id);
return factory.CreateObjA(id, objB);
}
}
class Factory {
public ClassB GetObjBById(string id) {
// ...
}
public ClassA CreateObjA(string id, ClassB objB) {
// ...
}
}
这里的主要优点是它将RepositoryA类与RepositoryB类分离。 (所以我们保留了S原则,让代码更加模块化和可重用)。
此外,它还可以更轻松地测试 RepositoryA 类,因为我们可以模拟 Factory 类,并且还可以更轻松地更改 ClassB 对象的实现,而不会影响 RepositoryA 类。
在这种情况下,您应该使用事件调度程序模式。
在 RepoA 上执行删除操作后,您可以调度如下事件:
dispatch repositoryA.deleted(RecordA)
它将保存已删除记录的信息。
然后,通风监听器将订阅此类事件,并且将存储库 B 作为依赖项,然后将调用删除。
让我们使用 B 作为实体名称,监听器声明应该听起来像:
Listen RepositoryA.delete and invoke onDelete(Event)
通过这种方法,您已经实现了 repoA 和 repoB 之间的松散耦合(强制执行打开/关闭原则 - 在关闭侧 -),因此 repoA 现在(再次)
问候。