Tôi đã theo dõi một số hướng dẫn về cách thiết kế API REST, nhưng tôi vẫn còn một số dấu hỏi lớn. Tất cả các hướng dẫn này cho thấy các tài nguyên với hệ thống phân cấp tương đối đơn giản và tôi muốn biết làm thế nào các nguyên tắc được sử dụng trong những nguyên tắc đó áp dụng cho một thứ phức tạp hơn. Hơn nữa, họ ở mức rất cao / kiến trúc. Họ hầu như không hiển thị bất kỳ mã có liên quan, chứ chưa nói đến lớp kiên trì. Tôi đặc biệt quan tâm đến tải / hiệu suất cơ sở dữ liệu, như Gavin King đã nói :
bạn sẽ tiết kiệm công sức nếu bạn chú ý đến cơ sở dữ liệu ở tất cả các giai đoạn phát triển
Hãy nói rằng ứng dụng của tôi sẽ cung cấp đào tạo cho Companies
. Companies
có Departments
và Offices
. Departments
có Employees
. Employees
có Skills
và Courses
, và một Level
số kỹ năng nhất định được yêu cầu để có thể đăng ký một số khóa học. Hệ thống phân cấp như sau, nhưng với:
-Companies
-Departments
-Employees
-PersonalInformation
-Address
-Skills (quasi-static data)
-Levels (quasi-static data)
-Courses
-Address
-Offices
-Address
Đường dẫn sẽ là một cái gì đó như:
companies/1/departments/1/employees/1/courses/1
companies/1/offices/1/employees/1/courses/1
Lấy tài nguyên
Vì vậy, ok, khi trở lại một công ty, tôi rõ ràng không trả lại toàn bộ phân cấp companies/1/departments/1/employees/1/courses/1
+ companies/1/offices/../
. Tôi có thể trả về một danh sách các liên kết đến các phòng ban hoặc các phòng ban được mở rộng và phải thực hiện cùng một quyết định ở cấp độ này: tôi có trả lại danh sách các liên kết cho nhân viên của bộ phận hoặc nhân viên mở rộng không? Điều đó sẽ phụ thuộc vào số lượng phòng ban, nhân viên, v.v.
Câu hỏi 1 : Suy nghĩ của tôi có đúng không, "nơi cắt giảm thứ bậc" là một sự phân rã kỹ thuật điển hình mà tôi cần thực hiện?
Bây giờ hãy nói rằng khi được hỏi GET companies/id
, tôi quyết định trả về một danh sách các liên kết đến bộ sưu tập của bộ phận và thông tin văn phòng mở rộng. Các công ty của tôi không có nhiều văn phòng, vì vậy việc tham gia với các bảng Offices
và Addresses
không nên là vấn đề lớn. Ví dụ về phản ứng:
GET /companies/1
200 OK
{
"_links":{
"self" : {
"href":"http://trainingprovider.com:8080/companies/1"
},
"offices": [
{ "href": "http://trainingprovider.com:8080/companies/1/offices/1"},
{ "href": "http://trainingprovider.com:8080/companies/1/offices/2"},
{ "href": "http://trainingprovider.com:8080/companies/1/offices/3"}
],
"departments": [
{ "href": "http://trainingprovider.com:8080/companies/1/departments/1"},
{ "href": "http://trainingprovider.com:8080/companies/1/departments/2"},
{ "href": "http://trainingprovider.com:8080/companies/1/departments/3"}
]
}
"name":"Acme",
"industry":"Manufacturing",
"description":"Some text here",
"offices": {
"_meta":{
"href":"http://trainingprovider.com:8080/companies/1/offices"
// expanded offices information here
}
}
}
Ở cấp độ mã, điều này ngụ ý rằng (sử dụng Hibernate, tôi không chắc nó như thế nào với các nhà cung cấp khác, nhưng tôi đoán điều đó khá giống nhau) Tôi sẽ không đặt một bộ sưu tập Department
như một trường trong Company
lớp của mình , bởi vì:
- Như đã nói, tôi không tải nó với
Company
, vì vậy tôi không muốn tải nó một cách háo hức - Và nếu tôi không tải nó một cách háo hức, tôi cũng có thể xóa nó, bởi vì bối cảnh tồn tại sẽ đóng sau khi tôi tải Công ty và không có lý do gì để cố tải nó sau đó (
LazyInitializationException
).
Sau đó, tôi sẽ đưa một người Integer companyId
vào Department
lớp, để tôi có thể thêm một bộ phận vào một công ty.
Ngoài ra, tôi cần lấy id của tất cả các phòng ban. Một cú đánh khác vào DB nhưng không phải là một cú nặng, nên sẽ ổn thôi. Mã này có thể trông như:
@Service
@Path("/companies")
public class CompanyResource {
@Autowired
private CompanyService companyService;
@Autowired
private CompanyParser companyParser;
@Path("/{id}")
@GET
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response findById(@PathParam("id") Integer id) {
Optional<Company> company = companyService.findById(id);
if (!company.isPresent()) {
throw new CompanyNotFoundException();
}
CompanyResponse companyResponse = companyParser.parse(company.get());
// Creates a DTO with a similar structure to Company, and recursivelly builds
// sub-resource DTOs such as OfficeDTO
Set<Integer> departmentIds = companyService.getDepartmentIds(id);
// "SELECT id FROM departments WHERE companyId = id"
// add list of links to the response
return Response.ok(companyResponse).build();
}
}
@Entity
@Table(name = "companies")
public class Company {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String industry;
@OneToMany(fetch = EAGER, cascade = {ALL}, orphanRemoval = true)
@JoinColumn(name = "companyId_fk", referencedColumnName = "id", nullable = false)
private Set<Office> offices = new HashSet<>();
// getters and setters
}
@Entity
@Table(name = "departments")
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer companyId;
@OneToMany(fetch = EAGER, cascade = {ALL}, orphanRemoval = true)
@JoinColumn(name = "departmentId", referencedColumnName = "id", nullable = false)
private Set<Employee> employees = new HashSet<>();
// getters and setters
}
Cập nhật tài nguyên
Đối với hoạt động cập nhật, tôi có thể hiển thị điểm cuối bằng PUT
hoặc POST
. Vì tôi muốn mình bình PUT
tĩnh, tôi không thể cho phép cập nhật một phần . Nhưng sau đó, nếu tôi muốn sửa đổi trường mô tả của công ty, tôi cần gửi toàn bộ đại diện tài nguyên. Điều đó dường như quá phồng lên. Tương tự khi cập nhật của nhân viên PersonalInformation
. Tôi không nghĩ việc gửi tất cả Skills
+ Courses
cùng với điều đó là hợp lý.
Câu hỏi 2 : PUT chỉ được sử dụng cho các tài nguyên hạt mịn?
Tôi đã thấy trong nhật ký rằng, khi hợp nhất một thực thể, Hibernate thực thi một loạt các SELECT
truy vấn. Tôi đoán đó chỉ là để kiểm tra xem có gì thay đổi hay không và cập nhật bất cứ thông tin nào cần thiết. Thực thể càng cao trong hệ thống phân cấp, các truy vấn càng nặng và phức tạp. Nhưng một số nguồn khuyên nên sử dụng tài nguyên hạt thô . Vì vậy, một lần nữa, tôi sẽ cần kiểm tra có bao nhiêu bảng quá nhiều và tìm ra sự thỏa hiệp giữa độ chi tiết tài nguyên và độ phức tạp của truy vấn DB.
Câu hỏi 3 : Đây chỉ là một sự phân rã kỹ thuật "biết cắt giảm" hay tôi đang thiếu thứ gì?
Câu hỏi 4 : Đây có phải là, hoặc nếu không, "quá trình suy nghĩ" đúng đắn khi thiết kế dịch vụ REST và tìm kiếm sự thỏa hiệp giữa mức độ chi tiết của tài nguyên, độ phức tạp của truy vấn và độ chói của mạng là gì?