我与同事就某些逻辑属于数据访问层还是业务逻辑层进行了争论。
这个场景是,BLL需要一些数据来处理。这些数据主要存在于数据库中。我们希望缓存这些数据(使用System.Runtime.Caching),以便在后续请求中快速可用。该体系结构是这样的:DAL和BLL位于同一个盒子和不同的程序集中(同一解决方案中的项目)。所以不用担心在电线上撞到DAL或类似的东西。
我的论点是,是命中缓存还是命中数据库的决定是DAL所关心的。业务逻辑层不应该关心数据来自哪里,而应该只关心它获得所需的数据。
他的论点是数据访问层应该是"纯粹的"one_answers"愚蠢的",任何决定访问缓存和数据库的逻辑都应该在业务逻辑层中。
在我看来,他所说的破坏了关注点分离,并导致了当目标是保持松散耦合时,层之间的耦合更加紧密。如果它是一个特定的程序/ui函数来决定去哪里获取数据,我可以看到BLL可能想要控制这一点,但这里的情况并非如此。这只是一个非常简单的缓存场景,其中数据库是主数据存储。
想法吗?
我完全同意你的看法。
缓存是DAL的一部分,不属于BLL。
让我们以hibernate为例,它使用缓存系统来存储实体的数据。Hibernate负责并且知道如何控制他的缓存,(脏读,刷新数据等)
你不希望你的BLL被这些低级的数据逻辑弄得乱七八糟。
对
我认为缓存应该在业务层完成。当您尝试从DAL获取数据时,您可以检查数据在cache system.runtime.caching中是否可用,然后使用缓存数据,否则从数据库中获取数据。此外,如果由于某种原因需要使缓存无效,可以稍后通过调用业务中的函数来实现。
将业务逻辑与数据分离的全部目的是,以便您可以在业务需求或技术更改时将它们交换出来。通过混合它们,你打败了这个逻辑,因此,在理论上你是正确的。然而,在现实世界中,我认为你需要更务实一点。应用程序的实际寿命有多长?技术发生变化的可能性有多大?要将两者完全分开需要做多少额外的工作?
我最初的反应和您的一样,让数据层缓存信息。这甚至可以与订阅数据库更改的策略集成在一起,或者实现轮询以确保数据保持最新。
但是,如果您打算在其他项目中重用数据层,或者即使不打算重用,在现有的业务层和数据层之间实现一个新的业务层来处理缓存决策可能也不是一个坏主意。因为最终,缓存不仅仅是一个性能问题,它还涉及到有关并发性和其他问题的业务决策。
n层系统就是,你不受你想要将事物分成多少层的限制。
我知道我迟到了两年多,但我想补充一些东西:
如果你为你的DAL定义了一个接口,你可以编写一个缓存机制,遵循这个接口,管理"缓存vs.命中数据源"的问题,而不需要担心技术或特定于源代码的DAL代码,也不需要担心BLL。例子:
internal interface IThingsGateway
{
public Thing GetThing(int thingId);
public void UpdateThing(ThingUpdateInfo info);
}
internal class MsSqlThingsGateway : IThingsGateway
{
// implementation specific to MsSql here
}
internal class CachingThingsGateway : IThingsGateway
{
private IThingsGateway underlyingImplementation;
public CachingThingsGateway(IThingsGateway implementation)
{
this.underlyingGateway = implementation;
}
public Thing GetThing(int thingId)
{
if (this.HasCachedThing(thingId))
{
return this.GetCachedThing(thingId);
}
var thing = this.underlyingGateway.GetThing(thingId);
this.SetCachedThing(thingId);
return thing;
}
public void UpdateThing(ThingUpdateInfo info)
{
this.underlyingGateway.UpdateThing(info);
this.ClearCachedThing(info.ThingId);
}
}
如果我需要检查多个数据源,我会使用相同的方法:编写IThingsGateway
的实现,处理杂耍各种数据源的逻辑,委托给适当的一个…然后在CachingThingsGateway
中包装。客户端代码最终将从一些工厂或容器获得IThingsGateway
引用,这是包装和实例化将发生的地方。
所有这些真的不需要那么多额外的努力。如果您使用缓存,那么无论如何都必须编写代码,并且将其放在具有相同接口的另一个类中所产生的开销在最坏的情况下是最小的。