我需要一些关于我的 API 设计的建议。 作为示例,请考虑以下后端实现。有一种继承结构可以从一个(抽象)公共基础定义一些更具体的实体。
现在我需要通过 webapi 公开这个模型。这应满足以下要求:
基本问题是:我应该构建 DTO 结构并依赖 DTO 类型信息,还是应该在路由中构建类型信息?
这是两个方法:
没有具体路线:
DTO 结构可能如下所示:
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 的类型信息来确定需要创建哪种具体类型。
有具体路线:
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)
在我的实际实现中,有两个以上的继承类。但出于什么原因您会建议采用哪种方法?
非常感谢。 托尼
我会尽可能避免继承。现实生活大多数时候都是不同事物的组合,清晰的继承极为罕见。它更像是类比而不是继承。例如,当某人既是教授又在大学学习时,你的上层模型就会崩溃。
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。
在我看来,继承是有帮助的。例如,考虑数据库表中所有表都共有的一些基本列,使用相应的抽象 EntityBase 类(id、user、changeuser、changeate、changeuser)进行建模。一般来说:继承是一种带有双站点磁带的组合吗?有时作品会不会看起来像是继承而来的?