用户特定的“结束日期”转换为DateTime应该在演示层还是业务层完成?

4
该系统有一个页面,用户可以通过指定起始日期和结束日期来搜索项目。这些是普通日期(没有时间组件)。对于用户来说,最直观的方式是将结束日期包含在内(因此也包括该结束日期的所有项目)。
然而,这些项目的CreateDate在数据存储中包含时间组件。实际上,这意味着我们需要将这个无时间概念的结束日期转换为第二天的0:00:00小时日期。这样我们就可以写出以下查询:
SELECT *
FROM   Items 
WHERE  CreateDate >= @STARTDATE
AND    CreateDate < @ENDDATE

将此结束日期转换的方法就像编写以下代码一样简单:
endDate.Date.AddDays(1);

我的问题是:这段代码的最后一行应该被视为业务逻辑并放置在业务层中,还是应该将其视为“模型绑定逻辑”并将其放置在表示层中?
如果将其放置在BL中,则意味着BL知道表示层,因为提供值的方式是特定于接口的。另一方面,由于操作在业务层中定义为DTO,我也可以将此对象视为对表示层有用的接口。
这个问题可能是哲学性质的,因为可能有多种看待它的方法,而实际的转换代码是微不足道的。我很想听听您为什么认为它应该放在一个层中而不是另一个层中。
我不认为应用程序的架构对这个问题的答案有任何影响。但为了更全面地说明,该架构基于命令查询,演示层创建一个查询对象,由业务层处理。PL代码通常如下所示:
public Action Filter(DateTime start, DateTime end)
{
    var query = new GetItemsByStartEndEndDateQuery
    {
        StartDate = start.Date,
        EndDate = end.Date.AddDays(1)
    }

    var items = this.queryProcessor.Handle(query);

    return this.View(items);
}

或者,尽可能使用(MVC)模型绑定来简单地将命令和查询对象进行模型绑定(非常方便):

public Action Filter(GetItemsByStartEndEndDateQuery query)
{
    var items = this.queryProcessor.Handle(query);

    return this.View(items);
}

如果涉及多个用户(例如WCF层和MVC层),你的答案会改变吗?


2
我可能会考虑在业务层和表示层之间增加一个层,用于执行这些翻译。不过这是个好问题:我也很想听听专家们的想法。 - Neil Thompson
@NeilThompson:你会怎么称呼这个层?也许是模型绑定层?但是模型绑定不应该属于表示层吗? - Steven
1
@Steven,你可以称其为“应用服务”。 - Alex
1
我认为对于这么简单的事情来说,额外增加一层是过度设计了。如果在业务逻辑层的API和表现层之间存在更大的差异,你可以使用适配器模式,但我不确定是否需要将其作为一个架构层。 - Joe
@Xander:ApplicationServices,那就是我的CommandHandlers和QueryHandlers的所在地 :-) - Steven
3个回答

2
您的业务层所提供的服务语义应该有一份合同,其中包括对该合同的自动化测试。这份合同应该定义输入参数被解释和验证的方式,例如:
- 如果开始日期大于结束日期,结果是什么? - 可接受哪些日期范围(例如,对于 SQL Server,1/1/1753 之前的日期如何处理)? - 输入参数是否允许具有非零的时间部分,如果允许,如何处理时间部分(截断并仅使用日期;如果调用者包含时间部分,则抛出异常;或者允许调用者指定包括时间部分的范围)? - 范围是排他还是包含在内的? - 如何处理时区(例如,带有本地、UTC 或未指定的 Kind 的日期参数)?
如果此合同与表示层想要从用户获取输入的方式不匹配,则表示层可以进行映射以匹配合同。
当然,如果该合同与数据访问层所期望的日期范围不匹配,则业务层可以将其映射到数据访问层所需的任何内容。

我喜欢你的回答,因为你的实质意思是“这取决于API”。这让我意识到问题实际上更多地关乎我的业务层应该采用哪种最合理的实现方式,但这取决于这个API将如何被使用。如果有其他调用者(例如WCF Web服务),从BL中删除此转换可能更合适;而如果唯一的消费者是MVC应用程序,则可以在BL中执行转换,因为这样可以使用MVC模型绑定,并且可以避免手动创建查询。 - Steven
1
@Steven,你说得完全正确。你的BL的API应该足够通用,以支持已知(甚至可能预期的)客户端。如果你只有一个客户端,那么为什么不暴露一个方便该客户端的API呢?YAGNI和所有这些。 - Joe

1

我通常将那行代码和类似的代码放在业务/领域层或领域服务中。

无论它是什么:

endDate.Date.AddDays(1);

或者:

endDate.Date.AddDays(3);

这是一个关于业务的问题,应该在业务层或域服务中处理。如果应用程序架构已经正确解耦,那么可以轻松地更改和重新部署域层,而不会影响其他层(例如表示层)。


1
对我来说,主要问题在于页面上输入的日期必须根据它们代表时间间隔的开始或结束以不同的方式转换为时间戳。也就是说,这不是两种直接映射到彼此的简单约定,而是需要进行语义转换。
在我看来,这样的转换应该属于业务逻辑。但请注意,如果用户的计算机与服务器处于不同的时区,则问题可能并不那么明确。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接