2015-04-25 13 views
59

Tôi đã đọc về các chiến lược phiên bản dành cho API ReST và thứ gì đó không có trong số chúng xuất hiện để giải quyết là cách bạn quản lý mã cơ sở bên dưới.Làm cách nào để bạn quản lý codebase cơ bản cho API được phiên bản?

Giả sử chúng tôi đang thực hiện một loạt các thay đổi đối với API - ví dụ: thay đổi tài nguyên Khách hàng của chúng tôi để trả về các trường forenamesurname riêng thay vì một trường name. (Đối với ví dụ này, tôi sẽ sử dụng giải pháp phiên bản URL vì dễ hiểu các khái niệm có liên quan, nhưng câu hỏi cũng có thể áp dụng cho thương lượng nội dung hoặc tiêu đề HTTP tùy chỉnh)

Hiện tại chúng tôi có điểm cuối tại http://api.mycompany.com/v1/customers/{id} và một điểm khác điểm cuối không tương thích tại http://api.mycompany.com/v2/customers/{id}. Chúng tôi vẫn đang phát hành các bản sửa lỗi và cập nhật bảo mật cho API phiên bản 1 nhưng tính năng phát triển tính năng mới giờ đây tập trung vào v2. Làm cách nào để viết, thử nghiệm và triển khai các thay đổi đối với máy chủ API của chúng tôi? Tôi có thể thấy ít nhất hai giải pháp:

  • Sử dụng chi nhánh/thẻ kiểm soát nguồn cho codebase v1. v1 và v2 được phát triển và được triển khai độc lập với các sửa đổi kiểm soát được sử dụng khi cần thiết để áp dụng cùng một lỗi cho cả hai phiên bản - tương tự như cách bạn quản lý các mã nguồn cho ứng dụng gốc khi phát triển phiên bản chính mới trong khi vẫn hỗ trợ phiên bản trước.

  • Làm cho chính codebase nhận thức được các phiên bản API, vì vậy bạn kết thúc với một mã đơn lẻ bao gồm cả biểu diễn khách hàng v1 và đại diện khách hàng v2. Coi phiên bản như một phần của kiến ​​trúc giải pháp của bạn thay vì vấn đề triển khai - có thể sử dụng một số kết hợp của không gian tên và định tuyến để đảm bảo yêu cầu được xử lý bởi phiên bản chính xác.

Ưu điểm rõ ràng của mô hình chi nhánh là nó tầm thường để xóa các phiên bản API cũ - chỉ dừng triển khai các chi nhánh/thẻ thích hợp - nhưng nếu bạn đang chạy một số phiên bản, bạn có thể kết thúc với một thực sự phức tạp cấu trúc nhánh và đường ống triển khai. Mô hình "codebase thống nhất" tránh được vấn đề này, nhưng (tôi nghĩ vậy?) Sẽ làm cho nó khó khăn hơn nhiều trong việc loại bỏ các tài nguyên không được chấp nhận và các điểm cuối khỏi codebase khi chúng không còn cần thiết nữa. Tôi biết điều này có lẽ chủ quan vì không có khả năng là một câu trả lời đúng, nhưng tôi tò mò muốn hiểu cách các tổ chức duy trì các API phức tạp trên nhiều phiên bản đang giải quyết vấn đề này.

+18

Cảm ơn bạn đã đặt câu hỏi này! Tôi KHÔNG THỂ tin rằng nhiều người hơn không trả lời câu hỏi này !! Tôi bị bệnh và mệt mỏi vì mọi người đều có ý kiến ​​về việc các phiên bản tham gia vào một hệ thống như thế nào, nhưng không ai có thể giải quyết được vấn đề khó khăn trong việc gửi các phiên bản tới mã thích hợp của họ. Bởi bây giờ nên có ít nhất một loạt các "mẫu" được chấp nhận hoặc "giải pháp" cho vấn đề này dường như phổ biến. Có một số câu hỏi điên rồ về SO liên quan đến "phiên bản API". Quyết định cách chấp nhận các phiên bản là FRIKKIN SIMPLE (tương đối)! Xử lý nó trong codebase một khi nó được vào, là HARD! – arijeet

Trả lời

22

Tôi đã sử dụng cả hai chiến lược bạn đề cập. Trong số đó, tôi ưu tiên cách tiếp cận thứ hai, đơn giản hơn, trong các trường hợp sử dụng hỗ trợ nó.Nghĩa là, nếu nhu cầu phiên bản rất đơn giản, sau đó đi với một thiết kế đơn giản hơn phần mềm:

  • Một số thấp của những thay đổi, những thay đổi phức tạp thấp, hoặc lịch trình thay đổi tần số thấp
  • Những thay đổi mà chủ yếu là trực giao với phần còn lại của codebase: API công cộng có thể tồn tại một cách hòa bình với phần còn lại của ngăn xếp mà không yêu cầu "quá mức" (cho bất kỳ định nghĩa nào của cụm từ mà bạn chọn áp dụng) phân nhánh trong mã

Tôi không thấy quá khó để xóa các phiên bản không dùng nữa bằng cách sử dụng mô hình này:

  • bảo hiểm thử nghiệm tốt có nghĩa là tách ra một API về hưu và mã ủng hộ liên quan đến đảm bảo không (tốt, tối thiểu) hồi quy
  • chiến lược đặt tên Tốt (tên gói API phiên bản, hoặc hơi xấu xí, phiên bản API trong tên phương pháp) giúp dễ dàng xác định mã có liên quan
  • Mối quan ngại về cắt ngang khó hơn; sửa đổi hệ thống phụ trợ lõi để hỗ trợ nhiều API phải được cân nhắc rất cẩn thận. Tại một số điểm, chi phí của phiên bản phụ trợ (Xem bình luận về "quá mức" ở trên) lớn hơn lợi ích của một codebase duy nhất.

Cách tiếp cận đầu tiên chắc chắn đơn giản hơn từ quan điểm giảm xung đột giữa các phiên bản hiện có, nhưng chi phí của việc duy trì các hệ thống riêng biệt có xu hướng lớn hơn lợi ích của việc giảm xung đột phiên bản. Điều đó nói rằng, nó đã chết đơn giản để đứng lên một ngăn xếp API công khai mới và bắt đầu lặp lại trên một nhánh API riêng biệt. Tất nhiên, tổn thất thế hệ được thiết lập gần như ngay lập tức, và các chi nhánh biến thành một mớ hỗn độn của việc sáp nhập, kết hợp các nghị quyết xung đột và những niềm vui khác như vậy. Một cách tiếp cận thứ ba là ở tầng kiến ​​trúc: áp dụng một biến thể của mẫu Mặt tiền, và trừu tượng hóa các API của bạn thành các khuôn mặt công khai, các phiên bản trao đổi với cá thể Facade thích hợp. của API. Mặt tiền của bạn (Tôi đã sử dụng Bộ điều hợp trong dự án trước) của mình trở thành gói riêng, độc lập và có thể kiểm tra, đồng thời cho phép bạn di chuyển các giao diện người dùng độc lập với chương trình phụ trợ và của nhau.

Điều này sẽ hoạt động nếu các phiên bản API của bạn có xu hướng phơi bày cùng một loại tài nguyên, nhưng với các biểu diễn cấu trúc khác nhau, như trong tên đầy đủ/tên/họ của bạn. Sẽ hơi khó khăn hơn nếu họ bắt đầu dựa vào các tính toán phụ trợ khác nhau, như trong "Dịch vụ phụ trợ của tôi đã trả về lãi suất hợp lý được tính không chính xác đã được hiển thị trong API công khai v1. Khách hàng của chúng tôi đã vá lỗi này. tính toán trong chương trình phụ trợ và áp dụng cho đến v2. Vì vậy, bây giờ chúng ta cần chia nhỏ mã tính lãi của mình. " May mắn thay, những người có xu hướng không thường xuyên: thực tế nói, người tiêu dùng của RESTful API ủng hộ sự đại diện tài nguyên chính xác qua khả năng tương thích ngược lỗi, thậm chí giữa những thay đổi không phá vỡ trên một nguồn tài nguyên về tài nguyên.

Tôi sẽ muốn nghe quyết định cuối cùng của bạn.

+2

Chỉ tò mò, trong mã nguồn, bạn có sao chép các mô hình giữa v0 và v1 không thay đổi không? Hoặc bạn có v1 sử dụng một số mô hình v0? Đối với tôi, tôi sẽ bị nhầm lẫn nếu tôi thấy v1 sử dụng các mô hình v0 cho một số trường. Nhưng mặt khác, nó sẽ làm giảm mã sưng lên. Để xử lý nhiều phiên bản, chúng ta chỉ phải chấp nhận và sống với mã trùng lặp cho các mô hình không bao giờ thay đổi? – EdgeCaseBerg

+0

Hồi ức của tôi là mã nguồn của chúng tôi đã phiên bản các mô hình độc lập với chính API, do đó, ví dụ API v1 có thể sử dụng Mô hình V1 và API phiên bản 2 cũng có thể sử dụng Mô hình V1. Về cơ bản, biểu đồ phụ thuộc nội bộ cho API công khai bao gồm cả mã API được tiếp xúc, cũng như mã "hoàn thành" phụ trợ như mã máy chủ và mô hình. Đối với nhiều phiên bản, chiến lược duy nhất mà tôi từng sử dụng là sao chép toàn bộ ngăn xếp - một phương pháp lai (mô-đun A được sao chép, mô-đun B được phiên bản ...) có vẻ rất khó hiểu. YMMV tất nhiên. :) – Palpatim

2

Phân nhánh có vẻ tốt hơn cho tôi và tôi đã sử dụng cách tiếp cận này trong trường hợp của mình.

Có thể bạn đã đề cập - việc sửa chữa lại các lỗi sẽ đòi hỏi một số nỗ lực, nhưng đồng thời hỗ trợ nhiều phiên bản dưới một cơ sở nguồn (với định tuyến và tất cả các thứ khác) sẽ yêu cầu bạn nếu không ít hơn. , làm cho hệ thống phức tạp hơn và quái dị với các nhánh khác nhau của logic bên trong (tại một số điểm của phiên bản bạn definetely sẽ đến lớn case() trỏ đến mô-đun phiên bản có mã trùng lặp, hoặc thậm chí còn tồi tệ hơn if(version == 2) then...). Cũng đừng quên rằng đối với các mục đích hồi quy, bạn vẫn phải kiểm tra phân nhánh.

Về chính sách phiên bản: tôi sẽ giữ tối đa -2 phiên bản từ hiện tại, không hỗ trợ cho các phiên bản cũ - điều đó sẽ cung cấp động lực cho người dùng di chuyển.

4

Đối với tôi, cách tiếp cận thứ hai tốt hơn. Tôi đã sử dụng nó cho các dịch vụ web SOAP và có kế hoạch sử dụng nó cho REST.

Khi bạn viết, codebase phải là phiên bản nhận thức, nhưng một lớp tương thích có thể được sử dụng làm lớp riêng biệt. Trong ví dụ của bạn, codebase có thể tạo ra biểu diễn tài nguyên (JSON hoặc XML) với tên và họ, nhưng lớp tương thích sẽ thay đổi nó để chỉ có tên thay thế.

Codebase chỉ nên triển khai phiên bản mới nhất, cho phép nói v3. Lớp tương thích sẽ chuyển đổi các yêu cầu và phản hồi giữa phiên bản mới nhất v3 và các phiên bản được hỗ trợ, ví dụ: v1 và v2. Lớp tương thích có thể có bộ điều hợp riêng biệt cho mỗi phiên bản được hỗ trợ có thể được kết nối dưới dạng chuỗi.

Ví dụ: Yêu cầu v1

Chủ đầu tư: V1 thích ứng với v2 ---> v2 thích ứng với v3 ----> codebase yêu cầu v2

Chủ đầu tư: V1 thích ứng với v2 (skip) - -> v2 thích ứng với v3 ----> codebase

Để đáp ứng các bộ điều hợp hoạt động đơn giản theo hướng ngược lại. Nếu bạn đang sử dụng Java EE, bạn có thể cho bạn chuỗi bộ lọc servlet làm chuỗi bộ điều hợp chẳng hạn.

Xóa một phiên bản thật dễ dàng, xóa bộ điều hợp tương ứng và mã kiểm tra.

Các vấn đề liên quan