Web-API 和多态 DTO

问题描述 投票:0回答:2

我需要一些关于我的 API 设计的建议。 作为示例,请考虑以下后端实现。有一种继承结构可以从一个(抽象)公共基础定义一些更具体的实体。

Entity Structure

现在我需要通过 webapi 公开这个模型。这应满足以下要求:

  • 列出所有人员(基类信息)
  • 获取一个人/特定类型的详细信息
  • 创建新学生/人

基本问题是:我应该构建 DTO 结构并依赖 DTO 类型信息,还是应该在路由中构建类型信息?

这是两个方法:

没有具体路线:

DTO 结构可能如下所示:

DTO with inheritance

API 可能如下所示:

api/persons/ [GET] -> PersonDTO GetAllPersons()
api/persons/{id} -> PersonDetailsDTO GetDetails(id) 
api/persons/ [POST] -> CreatePerson(CreatePersonDTO) //Need to switch case the concrete type of the DTO>

这依赖于 dto 的类型信息来确定需要创建哪种具体类型。

有具体路线:

DTO结构不需要继承: DTO without inheritance

API 可能如下所示:

api/persons/ [GET] -> PersonDTO GetAllPersons()
api/persons/students/{id} -> StudentDetailsDTO GetStudentDetails(id)
api/persons/professors/{id} -> ProfessorDetailsDTO GetProfessorDetails(id)
api/persons/students/ [post] -> CreateStudent(StudentDetailsDTO)
api/persons/professors/ [post] -> CreateProfessor(CreateProfessorDTO)

在我的实际实现中,有两个以上的继承类。但出于什么原因您会建议采用哪种方法?

非常感谢。 托尼

rest asp.net-web-api api-design webapi
2个回答
1
投票

我会尽可能避免继承。现实生活大多数时候都是不同事物的组合,清晰的继承极为罕见。它更像是类比而不是继承。例如,当某人既是教授又在大学学习时,你的上层模型就会崩溃。

OOP 中存在组合优于继承的问题。 DTO 应该使用继承还是组合

或者 DDD 有界上下文在更大范围内解决相同的问题。在这种情况下,他们会使用相同的 ID 制作单独的实体,有时他们甚至复制属性而不是制作复合材料。

OOP 和 DDD 方法都可以根据您的情况发挥作用。

对于 API,将其视为一堆操作。它不是一个 ORM,它是一个服务。那么您的服务需要什么?这真的是关于列出学生和教授吗?如果是这样,那么您需要服务做什么?换句话说,它只是一个数据库或贫血域模型。人类或机器会消耗它吗?他们需要什么功能?有时你需要询问消费者才能确定。很多时候,实施什么并不是您的决定,而是业务决策。

就您当前的API而言,可以简化:

api/persons/ [GET] -> PersonDTO GetAllPersons()
api/persons/{id} [GET] -> PersonDTO GetPersonDetails(id)
api/students/ [post] -> CreateStudent(StudentDetailsDTO)
api/students/{id} -> StudentDetailsDTO GetStudentDetails(id)
api/professors/ [post] -> CreateProfessor(CreateProfessorDTO)
api/professors/{id} -> ProfessorDetailsDTO GetProfessorDetails(id)

您可以提供嵌套资源,例如:

{
    link: "/student/1",
    type: "/docs/Student",
    id: 1,
    studentNumber: 123,
    averageMark: "idk",
    person: {
        type: "/docs/Person",
        link: "/persons/1",
        name: {
            link: "/persons/1/name",
            type: "/docs/Name",
            firstName: "Frédéric François",
            secondName: "Chopin"
        },
        contact: {
            link: "/persons/1/contact",
            type: "/docs/ContactDetails",
            phoneNumber: "1235463465",
            emailAddress: "[email protected]"
        },
        changeEmailAddress: {
            type: "/docs/Hyperlink",
            method: "PUT",
            uri: "/persons/{id}/contact/emailAddress",
            body: "{emailAddress}",
            input: {
                id: 1,
                emailAddress: {
                    type: "/docs/EmailAddress",
                    required: true
                }
            }
        }
    }
}

并从客户端使用类似

person.changeEmailAddress(inputs.newEmailAddress)
的内容。尽管大多数类和链接描述应该在文档中。我刚刚添加了链接,这样也许您就明白了。这里通常有一个图,它甚至不是层次结构或非循环的。您可以将 GET 链接扩展至首选级别。 RDF 更适合它,但很难用 RDF 词汇正确地描述事物,这就是它不受欢迎的原因。甚至还有 REST 词汇的 W3C 草案:https://www.Hydra-cg.com/spec/latest/core/ 还有带有 JSON 响应的更简单的框架。尽管据我所知,它们在 POST、PUT 等链接描述中都不够好。大多数时候,API 不提供链接,而是记录链接并让客户端硬编码 URI 模板、操作名称等。大多数开发人员甚至不明白统一接口和 HATEOAS 是强制性约束。如果不满足这些要求,他们正在开发的东西就不是 REST。


0
投票

在我看来,继承是有帮助的。例如,考虑数据库表中所有表都共有的一些基本列,使用相应的抽象 EntityBase 类(id、user、changeuser、changeate、changeuser)进行建模。一般来说:继承是一种带有双站点磁带的组合吗?有时作品会不会看起来像是继承而来的?

© www.soinside.com 2019 - 2024. All rights reserved.