软件架构问题。 BL按类型创建DTO的方式不同。有更好的吗?

时间:2010-11-01 23:13:16

标签: design-patterns architecture prototype dto

我有一个有三层的应用程序: 1.数据层:公开实体框架实体 2.业务逻辑层:查询EF模型,获取实体并暴露DTO(数据传输对象) 3. UI层:查询BL,获取DTO并查看它们。

现在我有一个问题。我的应用程序的不同部分需要相同的DTO,但有一些不同的字段。为简单起见,假设我的一个BL类暴露了名为Person的DTO,其中Name和Surname需要一次,并且在其他地方显示名称和出生日期。

我想听听您对我的简单解决方案的看法。我已经来了,我的UI必须在“DTO合同”上同意BL,以便两层在课堂上达成一致。在我的例子中,我会:

a)创建一个抽象的Person类。这个类没有任何方法也没有字段 b)在BL中创建一个名为GetPerson的方法,该方法接受Person类作为参数 c)定义两个或多个派生自Person的类(假设PersonName和PersonDOB) d)我的UI调用GetPerson传递所需类型(如GetPerson(typeof(PersonName))) e)BL填写Person类

你怎么看?有没有更好的解决方案?我认为这不是那么好买不到我想的更好。

非常感谢。 马可

2 个答案:

答案 0 :(得分:4)

我的MVC2应用程序中有类似的架构。我做了什么:

  1. BL返回具有Name,Surname和DoB的Person DTO(所有这些属性都是Person的一部分)

  2. 我的视图使用模型,因此我为每个视图创建特定模型。因此我会有2个人模型,即。一个是Name和Surname,另一个是Name和DoB。

  3. DTO和模型之间的转换是由一组我称之为Adapters的类完成的。为了简化代码,我使用 Automapper 。这是一段精彩的代码,它将通过考虑命名约定和显式配置将属性从DTO复制到模型。请看一下,因为您可能想用它来填充EF类中的DTO。

    总结一下,我有一个没有任何气味的一致BL(这个'给我一个这种类型的子类'业务对我来说有点嗅觉),我的观点正在使用只包含相关数据的强类型模型

答案 1 :(得分:2)

有两件事:首先是术语。我不是要成为一个术语纳粹 - 只是存在一些微妙的差异,可能会影响你对你的问题的看法。

DTO与POCO

当谈到DTO(数据传输对象)时,它听起来更像是POCO(普通旧CLR对象)。

波苏斯:

  • 是一种简单的数据结构,用于在层之间传递信息。
  • 通常这发生在应用程序中;特别是.Net应用程序(托管代码)。
  • 它们的设计通常考虑SRP(单一责任原则)。

在此背景下(特定POCO的设计)SRP将意味着“业务”驱动的使用案例(即:显示搜索结果中“人”数据的摘要)或通用/数据中心(即:提供'人')。

DTO:

直到最近我才盲目地认为POCO == DTO;当然很多人(我认为)倾向于以这种方式谈论它们;然后我读了Martin Fowler对DTO的定义,这是不同的。

  • 如果在应用程序的各层之间使用POCO,则在应用程序之间使用DTO。
  • 理由是,如果你必须通过网络发送数据 - 并且该调用很昂贵 - 那么不要进行多次调用以传递几个对象/数据/无论你将它们全部包装到一个实体中并进行一次调用。那个实体是DTO。

因此,可以想象您可以使用DTO将多个POCO传递给外部服务。

您的问题的答案

设计POCO(以及层和组件之间的接口)时,最重要的应该是“为什么”?在设计POCO(和DTO)时,您会考虑一些不同的观点和动机:

  • 责任:谁“拥有”POCO? (即:哪个系统拥有其中的数据)。
  • 使用案例:那里的POCO是因为它以一般/通用方式有意义,还是出于特定/专门目的?
  • 性能和其他运行时系统质量:DTO肯定受到性能考虑的影响,并且可以想象在层间交换信息时可以将类似的思想应用于POCO。

所以,您可以考虑以下两种方法......

使用混凝土POCO驱动的责任

设计并构建一个POCO(如在实际的类或结构中),完成你想要的工作;这将沿着上面讨论的路线(“业务”或“通用/以数据为中心”)。

这就是我目前的做法。我经常有一个'胖'POCO,它定义了一个实体(有时包括其他POCO),以及一个用于列表的'瘦'POCO。我也倾向于使用单独的POCO进行保存和更新。

虽然标准化为什么POCO的设计方式是有意义的(例如:它们都是由域模型驱动的,每个实体只有一个POCO) - 事实是你会有不同的动机来自不同的方向;我的建议是屈服于此,否则你最终会得到一个不灵活,易于维护或高效的系统。

使用界面

这是一种更“纯粹”的方法。而不是让您的应用程序通过使用接口来传递具体的POCO。然后,当您构建实现POCO时,它可以根据您的需要实现尽可能多的接口。例如:

public interface PersonID
{
    Guid PersonID { get; }
}

public interface PersonFullName
{
    string FirstName { get; }
    string Lastname { get; }
    string Honorific { get; }
}

public interface PersonDateOfBirth
{
    DateTime DateOfBirth { get; }
}

奖励积分

你没有要求这个(至少没有直接),但我不会将我的BL绑定到EF - 实际上我根本不会将它绑定到数据访问层。如果你这样做,任何想要使用BL的东西都将与EF绑在一起。您可能需要考虑Dependency Inversion