1)当Domain layer使用Infrastructure Service IS时,它的接口定义在Domain layer,而它的实现定义在Infrastructure layer。
我们不应该将IS(例如存储库或电子邮件服务)直接注入域实体:
class Foo
{
IRepository repo;
...
public int DoSomething()
{
var info = repo.Get...;
...
}
}
相反,如果域实体的某个方法需要特定的IS,则应用层可以将该IS作为参数传递给该方法:
class Foo
{
...
public int DoSomething(IRepository repo)
{
var info = repo.Get...;
...
}
}
a) 我认为IS也不应该直接注入到域服务中:
class TransferService
{
IRepository repo;
...
public bool Transfer()
{
var info = repo.Get...;
...
}
}
,而是应该将IS作为参数传递给打算使用它的域服务方法:
class TransferService
{
public bool Transfer(IRepository repo)
{
var info = repo.Get...;
...
}
}
b)我假设如果域实体的方法需要使用域服务,它不需要通过参数接收它(尽管将域服务作为参数传递具有显式传达方法依赖项的好处),而是可以调用直接,原因是域实体和域服务都是域概念:
class Foo
{
...
public int DoSomething()
{
var info = TransferService.Transfer(...);
...
}
}
更新:
1)
如果 IS 需要它的功能,可以将它注入到域服务中 - 即不传递给方法。这与实体不同,这是因为域服务是无状态的,因此可以一次性配置所需的依赖项并在需要时使用,例如由其他实体使用。
a) 不应该将IS注入域实体的主要原因是它们的状态性质?
b)但我认为不将IS注入域实体的主要原因是因为它会违反持久性无知规则?
c)如果我们用IS注入实体的状态性质会导致什么问题?
d) 将IS注入域服务是否违反 PI?如果不是,为什么不呢?
2)
域服务应该作为参数传递给实体,就像您将存储库接口作为参数传递一样。同样的原则也适用。
但是与存储库不同,域服务是一个域概念,那么“同样的原则适用”是什么意思?也就是说,将域服务注入域实体不会违反PI,而注入存储库会!
第二次更新:
1)
一种)
这是原因之一。另一个是创建不必要的耦合。如果实体上的单个行为只需要一个存储库,为什么总是将它注入实体?另外,现在您必须考虑如何解决这些依赖关系?使实体成为依赖注入图的一部分?这很快就会使实体的职责超载。
因此,如果我的理解正确 - 将IS注入域服务 DS并不违反 SRP,因为DS使用它来执行其指定任务(即其指定责任),而将IS注入域实体违反 SRP,因为主要责任Domain Entity的重点是其生命周期和身份,而IS大多数时候不是管理这两个任务的组成部分(即关注生命周期和身份)?
b)
您仍然可以将 IS 传递给域实体方法,这里的问题不会违反 PI,因为您传递的是接口,而不是实现。如果域方法仅在 IS 接口上使用一种方法,则问题将违反 SRP。
我-但是在您之前的几篇文章中,您注意到可以将IS作为参数传递给Domain Entity的方法,但是在这里您说如果此域方法在IS实例上仅使用一种方法,它将违反 SRP?
II - 如果IS实现了一个包含单个方法的基于角色的接口,而我们将这个基于角色的接口作为参数传递给域方法,你还会认为这违反了 SRP 吗?如果不是,为什么不呢?
d)
PI 是通过使用接口来维护的。
我读过几次,即使域方法通过接口引用存储库,它仍然被认为是违反 PI。你为什么不同意呢?
2)
然而,最好是非常明确的。因此,与其传递存储库并隐含地理解它恰好为实体提供服务,不如将提供的功能声明为它自己的接口并让实体依赖它。
a) 没有将域服务注入域实体的唯一原因是违反 SRP?
b)
将提供的功能声明为其自己的接口
我假设您建议使用基于角色的界面?但是,甚至不会将基于角色的接口(由说Domain Service实现)注入到Domain Entity中会导致违反 SRP,因为正如您在1a中指出的那样,注入的功能很可能只需要Domain Entity的单一行为? !
在将IS传递给域实体的方法方面,您和 Aaron Hawkins 似乎是对立的?!
第三次更新:
1)
一种)
因此,如果我的理解正确 - 将 IS 注入域服务 DS 并不违反 SRP,因为 DS 使用它来执行其指定任务(即其指定责任),而将 IS 注入域实体违反 SRP,因为主要责任域实体的重点是其生命周期和身份,而IS大多数时候不是管理这两个任务的组成部分(即关注生命周期和身份)?
是的,这是正确的,也是主要原因之一。
I - 从远处看,通过将IS注入域实体 DE似乎完全合理,该DE将违反 SRP,因为IS不会有助于管理指定给DE的两个任务。
但是当试图更详细地想象这种情况时,它会有点困难。也就是说,如果DE的方法专注于管理两个指定任务(即它的生命周期和身份),那么如果这些方法之一需要IS,那么假设它需要IS来完成这两个指定任务不是合理的吗?不是与DE 的生命周期和身份无关的其他任务吗?如果是,那么我们怎么能声称DE违反了 SRP?
II - 我也很难想象管理DE 的生命周期和身份究竟意味着什么。对于初学者来说,一旦为DE分配了一个身份,这个身份就不会改变。那么我们需要管理它的身份呢?
III - 管理DE 的生命周期意味着什么?也许在DE上定义保持其数据一致的不变量或者......?
IV - 那么现实世界实体执行的所有其他任务(即那些与DE的生命周期和身份无关的任务)都应该从DE中分解出来并放入相关的对象中?
d)
如果 IS 实现了一个包含单个方法的基于角色的接口,而我们将这个基于角色的接口作为参数传递给域方法,你还会认为这违反了 SRP 吗?如果不是,为什么不呢?
这样做并不可怕,但它有可能违反 SRP 或更明确地由 guillaume31 - ISP 指定。
我不确定我们如何声称将IS注入DE可能会违反ISP,因为据我所知ISP只能被实现此接口的对象违反,而不是被注入此接口的实现的对象?
第四次更新:
我开始意识到 SRP 比我最初想象的更令人困惑
一种)
与实体相关的行为(通常需要状态更改)也应放入实体中。如果此类行为需要使用服务,请传入该服务,但通常会尝试将尽可能多的行为放入实体中。
IV – 1 以下行为方法不包括状态更改,但我认为它们也应该属于一个Dog
实体:
class Dog
{
...
void DoBark();
void DoFetch();
void DoGuard();
Breed GetBreed();
Pedigree GetPedigree();
Snack FavSnack();
}
IV – b)GetBreed
和GetPedigree
行为FavSnack
方法?如果是,那么 propertiesBreed
和Pedigree
也Snack
应该被视为行为,因为它们本质上提供了相同的功能(假设GetBreed
,GetPedigree
并且FavSnack
不做一些繁重的计算,而只是简单地返回对象):
class Dog
{
...
void DoBark();
void DoFetch();
void DoGuard();
Breed Breed { get{...} }
Pedigree Pedigree { get{...} }
Snack Snack { get{...} }
}
IV – c) 如果上述属性也有设置器,我们会说它们包含状态改变行为吗?
IV – d)
与实体相关的行为(通常需要状态更改)也应放入实体中。
但是,如果域实体的主要职责是管理其生命周期,那么不包括与管理生命周期无关的行为就违反了 SRP(在上面的示例中,诸如此类的方法Dog.DoBark
很可能与Dog
' s 生命周期)?!
d)
一世。
将 IS 传递给 DE 行为方法更好,但是如果 IS 接口有很多与手头行为无关的东西,则可能违反 SRP/ISP。这是 ISP 的基本前提——依赖关系应该建立在特定的接口上,而不是恰好包含所需功能的臃肿接口。
因此,如果IS作为参数传递给 DE 的行为方法之一确实有一些与手头行为无关的操作,但 DE 的方法M不使用任何与M应该处理的行为无关的IS方法,我们仍然认为 DE是否违反 SRP/ISP?
二、– 我明白你在说什么,但我的困惑源于这样一个事实,根据以下对 ISP 的定义,该术语应仅用于指定实现特定接口的对象ServObj违反 ISP,而ServObj中的对象注入违反 SRP(由于接收ServObj):
接口隔离原则与单一职责原则相似,都处理职责的凝聚力。实际上,ISP可以理解为将SRP应用于对象的公共接口。
在一定程度上,ISP可以被认为是单一责任原则的一个子集,或更具体的形式。然而,ISP 的视角转变检查了给定类或模块的公共 API。
谢谢你