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

发布于 2024-09-30 01:21:37 字数 1431 浏览 0 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

假情假意假温柔 2024-10-07 01:21:37

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

  1. BL 返回一个具有姓名、姓氏和 DoB 的 Person DTO(所有这些属性都是 Person 的一部分)

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

DTO 和模型之间的转换是由一组我称为适配器的类完成的。为了简化代码,我使用Automapper。这是一段出色的代码,它将考虑命名约定和显式配置,将属性从 DTO 复制到模型。请查看 is,因为您可能希望使用它来填充 EF 类中的 DTO。

总而言之,我有一个一致的 BL,没有任何异味(这种“给我一个这种类型的子类”业务对我来说有点异味),并且我的观点是使用仅包含相关数据的强类型模型。

I have a similar architecture in my MVC2 app. What I did:

  1. BL returns a Person DTO that have Name, Surname and DoB (all those properties are a part of Person)

  2. My Views work with Models, so I create specific models for each view. I would therefore have 2 Person models, ie. one with Name and Surname, other with Name and DoB.

The conversion between DTOs and models is done by a set of classes I call Adapters. In order to simplify code I use Automapper. This is a brilliant piece of code that will copy properties from your DTOs to Models by taking into account naming conventions and explicit configuration. Do take a look at is, as you might want to use it to populate DTOs from your EF classes.

Summing up, I have a consistent BL without any smells (this 'give me a subclass of this type' business is a bit of smell to me), and my views are working with strongly-typed models that contain only relevant data.

夜还是长夜 2024-10-07 01:21:37

有几件事:首先是术语。我并不是想成为一个术语纳粹分子 - 只是存在一些细微的差异,可能会影响您对问题的看法。

DTO 与 POCO

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

POCO:

  • 是一种用于在层之间传递信息的简单数据结构。
  • 通常这发生在应用程序内;特别是 .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。您可能需要考虑 依赖倒置

A couple of things: firstly terminology. I'm not trying to be a terminology nazi - it's just that there's some subtle differences which might affect how you think about your problem.

DTO vs POCO

When you talk of a DTO (Data Transfer Object) it sounds more like a POCO (Plain Old CLR Object).

POCOs:

  • Are a simple data structure for passing information between layers.
  • Usually this happens within an application; specifically a .Net application (managed code).
  • They are usually designed with SRP (Single Responsibility Principle) in mind.

In this context (the design of a specific POCO) SRP would mean either a "business" driven case of use (i.e: to show a summary of 'person' data in search results) or a generic / data centric one (i.e: provide a 'person').

DTO:

Until recently I blindly assumed that POCO == DTO; and certainly a lot of people (I think) tend to talk about them that way; I then read Martin Fowler's definition of a DTO, which is different.

  • Where a POCO is used between layers of an application, a DTO is used between applications.
  • The rationale is that if you have to send data over the wire - and that call is expensive - then rather than making several calls to pass several objects/data/whatever you wrap them all into a single entity and make one call. That one entity is a DTO.

So, conceivably you might use a DTO to pass several POCO's to an external service.

The Answer to Your Question

When designing POCOs (and the interface between layers and components) at the top of your mind should be "why"? There's a few different views and motivations that you consider when designing your POCOs (and DTOs):

  • Responsibility: who "owns" the POCO? (i.e: what system owns the data within it).
  • Use Case: is the POCO there because it make sense in a general / generic way, or, is it there for a specific / specialized purpose?
  • Performance, and other runtime system qualities: DTO's are certainly influenced by performance considerations, and it's conceivable that similar thinking can be applied to POCO's when exchanging info between layers.

So, here's two approaches you might consider...

Responsibility Driven using Concrete POCOs

Design and build a POCO (as in an actual class or struct) that does the job you want; this will be along the lines discussed above ("business" or "generic / data centric").

This is how I currently do things. I often have a 'fat' POCO that defines an entity in it's entirety(sometimes including other POCOs), and a 'thin' POCO for use in lists. I also tend to use seperate POCOs for saving and updating.

While it makes sense to standardize why POCOs are designed the way they are (e.g: they are all driven by the domain model, and there's only one POCO per entity) - the truth is that you'll have different motivations from different directions; my advice is to give in to this otherwise you'll end up with a system that is not flexible, easy to maintain or performant.

Using Interfaces

This is a more 'pure' approach. Rather than have your application pass concrete POCOs around use interfaces instead. Then when you build implement a POCO it can implement as many of those interfaces as you like. For example:

public interface PersonID
{
    Guid PersonID { get; }
}

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

public interface PersonDateOfBirth
{
    DateTime DateOfBirth { get; }
}

Bonus Point

You haven't asked for this (at least not directly) but I wouldn't tie my BL to EF - in fact I wouldn't tie it to the Data Access Layer at all. If you do, whatever wants to use your BL will be tied to the EF as well. You might want to consider Dependency Inversion.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文