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 层)时,您的答案会改变吗?

4

3 回答 3

2

应该有一个由业务层公开的服务语义契约,并且可能对该契约进行自动化测试。

该合约应定义如何解释和验证输入参数,例如:

  • 如果 StartDate > EndDate 会产生什么结果?
  • 什么样的日期范围是可以接受的(例如 SQL Server 的日期早于 1753 年 1 月 1 日)?
  • 是否允许输入参数具有非零时间组件,如果允许,如何处理时间(仅截断和使用日期;如果调用者包含时间组件则抛出异常;或允许调用者指定范围,包括一天中的时间部分)。
  • 范围是排他性的还是包含性的?
  • 如何处理时区(例如,Kind = Local、Utc 或未指定的日期参数)?

如果这个契约与表示层想要从用户那里获取输入的方式不匹配,那么表示层可以进行映射以匹配契约。

当然,如果合同与数据访问层期望的日期范围不匹配,业务层可以映射到数据访问层期望的任何内容。

于 2012-09-12T12:43:07.313 回答
1

对我来说,主要的一点是,页面上输入的日期必须根据它们是代表时间间隔的开始还是结束,以不同的方式转换为时间戳。也就是说,不是两个不同的约定直接相互映射的简单问题,而是要执行语义转换。

在我看来,这种转换属于业务逻辑。但是请注意,如果用户的计算机与服务器处于不同的时区,则问题可能会变得不那么明确。

于 2012-09-12T12:20:02.483 回答
1

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

无论是:

endDate.Date.AddDays(1);

或者:

endDate.Date.AddDays(3);

这是一个业务问题,应该在业务层或域服务中。给定一个适当解耦的应用程序架构,可以简单地更改和重新部署领域层,而不会影响其他层(如表示层)。

于 2012-09-12T12:01:49.003 回答