在 Web API 应用程序中分离模型

本文关键字:分离 模型 应用程序 Web API | 更新日期: 2023-09-27 18:36:57

我的团队使用实体框架开发一个 Web API 应用程序,Gui 由一个单独的团队开发。

我的问题是应该如何定义模型?我们是否应该有两个项目 - 一个用于域模型(数据库实体),一个用于可序列化的 Dtos?

从Dto到领域模型的解析应该在哪里发生,什么时候应该以相反的方式进行?

此外,有时需要将所有数据发送给客户端。是否也应该为这些情况创建一个 Dto?还是应该返回域模型?

在 Web API 应用程序中分离模型

一般来说,最好不要让实体(数据库模型)泄漏到数据库层中。但是,与软件中的所有内容一样 - 这可能会失败。其中一个缺点是它开始增加数据层的复杂性,因为它涉及将您的实体映射到数据库层内的 DTO,最终留下充满类似方法的存储库返回不同的 DTO 类型。

有些人还认为,当你开始将抽象泄漏到不同的层时,从数据层公开IQueryables也是一件坏事 - 尽管这似乎总是有点极端。

就个人而言,我更喜欢我认为

更务实的方法,我更喜欢使用 AutoMapper 这样的工具自动将我的实体映射到业务逻辑层中的 DTO。

例如:

// Initial configuration loaded on start up of application and cached by AutoMapper
AutoMapper.Mapper.CreateMap<BlogPostEntity, BlogPostDto>();
// Usage
BlogPostDto blogPostDto = AutoMapper.Mapper.Map<BlogPostDto>(blogPostEntity);

AutoMapper还能够配置更复杂的映射,尽管如果可能的话,您应该通过坚持使用扁平的DTO来避免这种情况。

此外,AutoMapper 的另一个重要功能是能够自动将实体投影到 DTO。这会导致 SQL 更加干净,其中仅查询 DTO 中的列:

public IEnumerable<BlogPostDto> GetRecentPosts()
{
    IEnumerable<BlogPostDto> blogPosts = this.blogRepository.FindAll().Project(this.mappingEngine).To<BlogPostDto>().ToList();
    return blogPosts;
}

此外,有时需要将所有数据发送给客户端。是否也应该为这些情况创建一个 Dto?还是应该返回域模型?

应该为这些创建 DTO。最终,您不希望客户端依赖于数据架构,这正是公开实体时将发生的情况。

替代方法:命令/查询隔离

我还应该强调,典型的分层体系结构还有其他一些替代方案,例如命令/查询分离方法,您可以在其中通过中介对命令和查询进行建模。我不会太详细地讨论它,因为它是一个完全不同的主题,但我绝对更喜欢上面讨论的分层方法。这将导致您将实体直接映射到建模命令或查询中的 DTO。

我建议看看 调解 为此。作者吉米·博加德(Jimmy Bogard)也创建了AutoMapper,他也有这个视频谈论同样的主题。

我在几个项目中都有类似的要求,在大多数情况下,我们至少分离了三层:

数据库层

数据库

对象是数据库表的简单一对一表示形式。没有别的。

域层

域层定义表示完整业务对象的实体对象。在我们的定义中,实体聚合了与实体直接关联的所有数据,不能被视为专用实体。

例如:在处理发票的应用程序中,您有一个表invoiceinvoice_items。业务逻辑读取这两个表,并将数据合并到实体对象Invoice

应用层

在应用层中,我们为要发送到客户端的所有类型的数据定义模型。传递域实体对象以节省时间很诱人,但严格禁止。发布任何不应该发布的数据的风险太高。此外,您还可以在 API 设计方面获得更多自由。这就是帮助您满足最后一个要求(将所有数据发送到客户端)的原因:刚刚构建了一个新模型,该模型聚合了您需要发送的所有域对象的数据。

这是我们在所有项目中使用的最小图层集。在数百种情况下,我们很高兴拥有几个抽象层,这为我们提供了足够的可能性来增强和扩展应用程序。