Làm thế nào để bạn tổ chức phần mềm tùy biến cao?


28

Tôi đang làm việc trong một dự án phần mềm lớn được tùy biến cao cho nhiều khách hàng khác nhau trên toàn thế giới. Điều này có nghĩa là chúng tôi có thể có 80% mã phổ biến giữa các khách hàng khác nhau, nhưng cũng có rất nhiều mã phải thay đổi từ khách hàng này sang khách hàng khác. Trước đây, chúng tôi đã phát triển các kho lưu trữ riêng biệt (SVN) và khi một dự án mới bắt đầu (chúng tôi có ít, nhưng khách hàng lớn) đã tạo một kho lưu trữ khác dựa trên bất kỳ dự án nào trong quá khứ có cơ sở mã tốt nhất cho nhu cầu của chúng tôi. Điều này đã hoạt động trong quá khứ, nhưng chúng tôi gặp phải một số vấn đề:

  • Lỗi được cố định trong một kho lưu trữ không được vá trong các kho khác. Đây có thể là một vấn đề của tổ chức, nhưng tôi cảm thấy khó khắc phục và vá một lỗi trong 5 kho lưu trữ khác nhau, hãy nhớ rằng nhóm duy trì kho lưu trữ này có thể ở một nơi khác trên thế giới và chúng tôi không có môi trường thử nghiệm của họ , không biết lịch trình của họ hoặc những yêu cầu họ có ("lỗi" ở một quốc gia có thể là "tính năng" ở một quốc gia khác).
  • Các tính năng và cải tiến được tạo cho một dự án, cũng có thể hữu ích cho một dự án khác bị mất hoặc nếu chúng được sử dụng trong dự án khác thường gây ra những cơn đau đầu lớn khi hợp nhất chúng từ cơ sở mã này sang cơ sở mã khác (vì cả hai chi nhánh có thể được phát triển độc lập trong một năm ).
  • Tái cấu trúc và cải tiến mã được thực hiện trong một nhánh phát triển hoặc bị mất hoặc gây hại nhiều hơn là tốt nếu bạn phải hợp nhất tất cả các thay đổi này giữa các nhánh.

Bây giờ chúng tôi đang thảo luận về cách giải quyết những vấn đề này và cho đến nay đã nảy ra những ý tưởng sau đây về cách giải quyết vấn đề này:

  1. Giữ sự phát triển trong các nhánh riêng biệt nhưng tổ chức tốt hơn bằng cách có một kho lưu trữ trung tâm nơi các sửa lỗi chung được hợp nhất và có tất cả các dự án hợp nhất các thay đổi từ kho lưu trữ trung tâm này thành cơ sở thường xuyên (ví dụ hàng ngày). Điều này đòi hỏi kỷ luật rất lớn và rất nhiều nỗ lực để hợp nhất giữa các chi nhánh. Vì vậy, tôi không tin rằng nó sẽ hoạt động và chúng ta có thể giữ kỷ luật này, đặc biệt là khi áp lực thời gian đặt vào.

  2. Từ bỏ các nhánh phát triển riêng biệt và có một kho lưu trữ mã trung tâm nơi tất cả các mã của chúng tôi sống và thực hiện tùy chỉnh của chúng tôi bằng cách có các mô-đun có thể cắm và các tùy chọn cấu hình. Chúng tôi đã sử dụng các thùng chứa Dependency Injection để giải quyết các phụ thuộc trong mã của chúng tôi và chúng tôi đang theo mẫu MVVM trong hầu hết các mã của chúng tôi để tách biệt logic nghiệp vụ khỏi UI của chúng tôi.

Cách tiếp cận thứ hai có vẻ thanh lịch hơn, nhưng chúng ta có nhiều vấn đề chưa được giải quyết trong phương pháp này. Ví dụ: làm thế nào để xử lý các thay đổi / bổ sung trong mô hình / cơ sở dữ liệu của bạn. Chúng tôi đang sử dụng .NET với Entity Framework để có các thực thể gõ mạnh. Tôi không thấy cách chúng tôi có thể xử lý các thuộc tính cần thiết cho một khách hàng nhưng vô dụng đối với một khách hàng khác mà không làm lộn xộn mô hình dữ liệu của chúng tôi. Chúng tôi đang nghĩ đến việc giải quyết vấn đề này trong cơ sở dữ liệu bằng cách sử dụng các bảng vệ tinh (có một bảng riêng trong đó các cột bổ sung của chúng tôi cho một thực thể cụ thể sống với ánh xạ 1: 1 đến thực thể ban đầu), nhưng đây chỉ là cơ sở dữ liệu. Làm thế nào để bạn xử lý này trong mã? Mô hình dữ liệu của chúng tôi sống trong một thư viện trung tâm mà chúng tôi sẽ không thể mở rộng cho mỗi khách hàng sử dụng phương pháp này.

Tôi chắc chắn rằng chúng tôi không phải là nhóm duy nhất đấu tranh với vấn đề này và tôi bị sốc khi tìm thấy quá ít tài liệu về chủ đề này.

Vì vậy, câu hỏi của tôi là như sau:

  1. Bạn có kinh nghiệm gì với phần mềm tùy biến cao, bạn đã chọn phương pháp nào và cách thức hoạt động cho bạn?
  2. Cách tiếp cận nào bạn đề nghị và tại sao? Có một cách tiếp cận tốt hơn?
  3. Có bất kỳ cuốn sách hay bài viết hay về chủ đề mà bạn có thể giới thiệu?
  4. Bạn có đề xuất cụ thể cho môi trường kỹ thuật của chúng tôi (.NET, Entity Framework, WPF, DI) không?

Chỉnh sửa:

Cảm ơn tất cả những lời đề nghị. Hầu hết các ý tưởng phù hợp với những ý tưởng mà chúng tôi đã có trong nhóm của chúng tôi, nhưng thật sự hữu ích khi thấy trải nghiệm bạn có với chúng và các mẹo để thực hiện chúng tốt hơn.

Tôi vẫn không chắc chắn chúng tôi sẽ đi theo con đường nào và tôi sẽ không đưa ra quyết định (một mình), nhưng tôi sẽ vượt qua điều này trong nhóm của mình và tôi chắc chắn rằng nó sẽ hữu ích.

Tại thời điểm này, kỳ hạn dường như là một kho lưu trữ duy nhất sử dụng các mô-đun cụ thể của khách hàng. Tôi không chắc kiến ​​trúc của chúng tôi phụ thuộc vào điều này hay chúng tôi phải đầu tư bao nhiêu để làm cho nó phù hợp, vì vậy một số thứ có thể sống trong các kho riêng biệt trong một thời gian, nhưng tôi nghĩ đó là giải pháp dài hạn duy nhất sẽ hoạt động.

Vì vậy, cảm ơn một lần nữa cho tất cả các câu trả lời!


Xem xét việc xử lý các bảng cơ sở dữ liệu dưới dạng mã.

Chúng tôi đang làm điều này theo nghĩa là chúng tôi có các tập lệnh cơ sở dữ liệu trong kho lưu trữ lật đổ của chúng tôi, nhưng nó không thực sự giải quyết các vấn đề được đề cập ở trên. Chúng tôi không muốn có các bảng kiểu Khóa-Giá trị trong mô hình cơ sở dữ liệu của mình vì chúng có rất nhiều vấn đề. Vậy làm thế nào để bạn cho phép bổ sung vào mô hình của mình cho các khách hàng cá nhân trong khi vẫn duy trì kho lưu trữ mã được chia sẻ cho tất cả họ?
aKzenT

Câu trả lời:


10

Có vẻ như vấn đề cơ bản không chỉ là bảo trì kho mã, mà là thiếu kiến ​​trúc phù hợp .

  1. Cốt lõi / bản chất của hệ thống là gì, sẽ luôn được chia sẻ bởi tất cả các hệ thống?
  2. Những cải tiến / sai lệch nào được yêu cầu bởi mỗi khách hàng?

Một khung hoặc thư viện chuẩn bao gồm cái trước, trong khi cái sau sẽ được triển khai như các phần bổ trợ (plugin, lớp con, DI, bất cứ điều gì có ý nghĩa đối với cấu trúc mã).

Một hệ thống kiểm soát nguồn quản lý các chi nhánh và phát triển phân tán có thể cũng sẽ giúp ích; Tôi là một fan hâm mộ của Mercurial, những người khác thích Git. Khung sẽ là nhánh chính, ví dụ, mỗi hệ thống tùy chỉnh sẽ là nhánh con.

Các công nghệ cụ thể được sử dụng để thực hiện hệ thống (.NET, WPF, bất cứ điều gì) phần lớn không quan trọng.

Có được quyền này là không dễ dàng , nhưng nó rất quan trọng cho khả năng tồn tại lâu dài. Và tất nhiên, bạn càng chờ đợi lâu, khoản nợ kỹ thuật bạn sẽ phải giải quyết càng lớn.

Bạn có thể thấy cuốn sách Kiến trúc phần mềm: Nguyên tắc tổ chức và mô hình hữu ích.

Chúc may mắn!


3
Vâng Kiến trúc thường là một thứ thuộc về tâm lý hơn là một thứ kỹ thuật. Nếu bạn tập trung hoàn toàn vào sản phẩm, thì bạn sẽ kết thúc với một sự cố công việc trong đó các thành phần chỉ hữu ích cho sản phẩm đó. Mặt khác, nếu bạn tập trung vào việc xây dựng một tập hợp các thư viện sẽ hữu ích hơn, thì bạn xây dựng một tập hợp các khả năng có thể được triển khai trong một phạm vi rộng hơn. Chìa khóa, tất nhiên, là tìm sự cân bằng phù hợp giữa hai thái cực này cho tình huống cụ thể của bạn.
William Payne

Tôi nghĩ rằng có một phần được chia sẻ lớn giữa nhiều chi nhánh của chúng tôi (> 90%), nhưng 10% cuối cùng luôn ở những nơi khác nhau, vì vậy có rất ít thành phần mà tôi không thể tưởng tượng được một số thay đổi cụ thể của khách hàng ngoại trừ một số thư viện tiện ích không chứa bất kỳ logic kinh doanh nào.
aKzenT

@aKzenT: hmmm ... đừng tưởng tượng, thay vào đó hãy đo. Khảo sát mã và xem xét tất cả các địa điểm đã xảy ra tùy chỉnh, lập danh sách các thành phần được sửa đổi, lưu ý tần suất từng thành phần đã được sửa đổi và suy nghĩ về các loại và mô hình sửa đổi đã thực sự được thực hiện. Họ là mỹ phẩm hay thuật toán? Họ đang thêm hoặc thay đổi chức năng cơ sở? Lý do cho mỗi loại thay đổi là gì? Một lần nữa, đây là công việc khó khăn , và bạn có thể không thích những tác động của những gì bạn khám phá.
Steven A. Lowe

1
Tôi chấp nhận đây là câu trả lời, bởi vì chúng tôi không thể đưa ra quyết định này trước khi suy nghĩ thêm về kiến ​​trúc của chúng tôi, xác định các phần phổ biến hoặc NÊN phổ biến và sau đó xem liệu chúng tôi có thể sống với một kho lưu trữ không hoặc nếu cần ít nhất là thời điểm này) Câu trả lời này phản ánh IMO tốt nhất này. Cảm ơn tất cả các bài viết khác, mặc dù!
aKzenT

11

Một công ty tôi từng làm việc có cùng một vấn đề và cách tiếp cận để giải quyết vấn đề là: Một khuôn khổ chung cho tất cả các dự án mới đã được tạo ra; điều này bao gồm tất cả những thứ phải giống nhau trong mọi dự án. Ví dụ: công cụ tạo biểu mẫu, xuất sang Excel, ghi nhật ký. Nỗ lực đã được thực hiện để đảm bảo rằng khung chung này chỉ được cải thiện (khi một dự án mới cần các tính năng mới), nhưng không bao giờ rẽ nhánh.

Dựa trên khung đó, mã dành riêng cho khách hàng được duy trì trong các kho riêng biệt. Khi hữu ích hoặc cần thiết, sửa lỗi và cải tiến được sao chép giữa các dự án (với tất cả các cảnh báo được mô tả trong câu hỏi). Mặc dù vậy, những cải tiến hữu ích trên toàn cầu đi vào khuôn khổ chung.

Có tất cả mọi thứ trong một cơ sở mã chung cho tất cả các khách hàng có một số lợi thế, nhưng mặt khác, việc đọc mã trở nên khó khăn khi có vô số ifs để làm cho chương trình hoạt động khác nhau đối với mỗi khách hàng.

EDIT: Một giai thoại để làm cho điều này dễ hiểu hơn:

Tên miền của công ty đó là quản lý kho, và một nhiệm vụ của hệ thống quản lý kho là tìm một vị trí lưu trữ miễn phí cho hàng hóa đến. Nghe có vẻ dễ dàng, nhưng trong thực tế, rất nhiều ràng buộc và chiến lược phải được quan sát.

Tại một thời điểm, quản lý đã yêu cầu một lập trình viên tạo ra một mô-đun linh hoạt, có thể tham số để tìm các vị trí lưu trữ, thực hiện một số chiến lược khác nhau và nên được sử dụng trong tất cả các dự án tiếp theo. Nỗ lực cao cả đã dẫn đến một mô-đun phức tạp, rất khó hiểu và duy trì. Trong dự án tiếp theo, trưởng dự án không thể tìm ra cách làm cho nó hoạt động trong kho đó và nhà phát triển mô-đun nói đã biến mất, vì vậy cuối cùng ông đã bỏ qua nó và viết một thuật toán tùy chỉnh cho nhiệm vụ đó.

Vài năm sau, bố cục của nhà kho nơi mô-đun này ban đầu được sử dụng đã thay đổi và mô-đun với tất cả tính linh hoạt của nó không phù hợp với các yêu cầu mới; vì vậy tôi cũng thay thế nó bằng một thuật toán tùy chỉnh ở đó.

Tôi biết LỘC không phải là một phép đo tốt, nhưng dù sao: kích thước của mô-đun "linh hoạt" là ~ 3000 LỘC (PL / SQL), trong khi một mô-đun tùy chỉnh cho cùng một nhiệm vụ mất ~ 100..250 LỘC. Do đó, cố gắng linh hoạt cực kỳ tăng kích thước của cơ sở mã, mà không đạt được khả năng sử dụng lại mà chúng tôi đã hy vọng.


Cảm ơn phản hồi của bạn. Đây là một phần mở rộng cho giải pháp đầu tiên được mô tả và chúng tôi cũng nghĩ về điều này. Tuy nhiên vì hầu hết các thư viện của chúng tôi sử dụng một số thực thể cốt lõi và các thực thể cốt lõi này thường được mở rộng cho một khách hàng hoặc một khách hàng khác, tôi nghĩ rằng chúng tôi chỉ có thể đặt rất ít thư viện trong kho lưu trữ cốt lõi này. Ví dụ: chúng tôi có một thực thể "Khách hàng" được xác định và ORM được ánh xạ trong một thư viện được sử dụng bởi hầu hết các thư viện và chương trình khác của chúng tôi. Nhưng mỗi khách hàng có một số trường bổ sung họ cần thêm vào "Khách hàng", vì vậy chúng tôi sẽ cần rẽ nhánh thư viện này và do đó tất cả các thư viện tùy thuộc vào nó.
aKzenT

3
Ý tưởng của chúng tôi để tránh vô số "ifs" là sử dụng rộng rãi việc tiêm phụ thuộc và trao đổi các mô-đun hoàn chỉnh cho các khách hàng khác nhau. Không chắc chắn làm thế nào điều này sẽ làm việc mặc dù. Cách tiếp cận này đã làm việc cho bạn như thế nào?
aKzenT

+1, điều này về cơ bản phù hợp với kinh nghiệm của tôi về các dự án đã xử lý việc này tốt. Nếu bạn sẽ sử dụng 'ifs cho các cách tiếp cận khách hàng khác nhau, 2 điểm: 1. Đừng làm nếu (khách hàng1) ..., thay vào đó hãy làm nếu (configureOption1) ... và có các tùy chọn cấu hình cho mỗi khách hàng. 2. Cố gắng không làm điều đó! Có thể 1% thời gian sẽ là tùy chọn tốt hơn (dễ hiểu hơn / dễ bảo trì hơn) so với việc có các mô-đun cụ thể cấu hình.
vaughandroid

@Baqueta: Chỉ cần làm rõ: bạn khuyên bạn nên sử dụng các mô-đun cho mỗi khách hàng thay vì các tùy chọn cấu hình (ifs), phải không? Tôi thích ý tưởng của bạn để phân biệt giữa các tính năng thay vì khách hàng. Vì vậy, sự kết hợp của cả hai sẽ có nhiều "mô-đun tính năng" khác nhau được điều khiển bởi các tùy chọn cấu hình. Một khách hàng sau đó chỉ được đại diện như một tập hợp các mô-đun tính năng độc lập. Tôi thích cách tiếp cận này rất nhiều, nhưng tôi không chắc làm thế nào để kiến ​​trúc sư này. DI giải quyết vấn đề tải và trao đổi mô-đun, nhưng làm thế nào để bạn quản lý các mô hình dữ liệu khác nhau giữa các khách hàng?
aKzenT

Yup, mô-đun cho mỗi tính năng và sau đó cấu hình / lựa chọn tính năng cho mỗi khách hàng. DI sẽ là lý tưởng. Thật không may, tôi chưa bao giờ phải kết hợp các yêu cầu của bạn về tùy chỉnh theo từng khách hàng đáng kể với một thư viện dữ liệu duy nhất, vì vậy tôi không chắc mình có thể giúp được gì nhiều ở đó ...
vaughandroid

5

Một trong những dự án tôi đã làm việc trên nhiều nền tảng được hỗ trợ (hơn 5) trên một số lượng lớn các bản phát hành sản phẩm. Rất nhiều thách thức bạn đang mô tả là những điều chúng tôi phải đối mặt, mặc dù theo một cách hơi khác. Chúng tôi đã có một DB độc quyền, vì vậy chúng tôi không gặp phải vấn đề tương tự trong lĩnh vực đó.

Cấu trúc của chúng tôi tương tự như của bạn, nhưng chúng tôi có một kho lưu trữ duy nhất cho mã của chúng tôi. Mã cụ thể của nền tảng đã đi vào các thư mục dự án của riêng họ trong cây mã. Mã phổ biến sống trong cây dựa trên lớp mà nó thuộc về.

Chúng tôi đã biên soạn có điều kiện, dựa trên nền tảng đang được xây dựng. Duy trì điều đó là một nỗi đau, nhưng nó chỉ phải được thực hiện khi các mô-đun mới được thêm vào ở lớp cụ thể của nền tảng.

Có tất cả các mã trong một kho lưu trữ giúp chúng tôi dễ dàng sửa lỗi trên nhiều nền tảng và phát hành cùng một lúc. Chúng tôi đã có một môi trường xây dựng tự động cho tất cả các nền tảng hoạt động như một điểm dừng trong trường hợp mã mới phá vỡ một nền tảng không liên quan được cho là.

Chúng tôi đã cố gắng ngăn cản nó, nhưng sẽ có trường hợp một nền tảng cần sửa chữa dựa trên một lỗi cụ thể của nền tảng có mã phổ biến khác. Nếu chúng ta có thể ghi đè lên trình biên dịch một cách có điều kiện mà không làm cho mô-đun trông khó hiểu, trước tiên chúng ta sẽ làm điều đó. Nếu không, chúng tôi sẽ chuyển mô-đun ra khỏi lãnh thổ chung và đẩy nó vào nền tảng cụ thể.

Đối với cơ sở dữ liệu, chúng tôi đã có một vài bảng có các cột / sửa đổi nền tảng cụ thể. Chúng tôi sẽ đảm bảo rằng mọi phiên bản nền tảng của bảng đều đáp ứng mức cơ bản của chức năng để mã phổ biến có thể tham chiếu nó mà không phải lo lắng về sự phụ thuộc của nền tảng. Các truy vấn / thao tác cụ thể của nền tảng đã được đẩy vào các lớp dự án nền tảng.

Vì vậy, để trả lời câu hỏi của bạn:

  1. Rất nhiều, và đó là một trong những đội hay nhất tôi từng làm việc cùng. Codebase tại thời điểm đó là khoảng 1 triệu loc. Tôi đã không chọn cách tiếp cận, nhưng nó khá hiệu quả. Ngay cả trong nhận thức muộn màng, tôi vẫn chưa thấy cách xử lý nào tốt hơn.
  2. Tôi đề nghị cách tiếp cận thứ hai mà bạn đề xuất với các sắc thái tôi đề cập trong câu trả lời của tôi.
  3. Không có cuốn sách nào tôi có thể nghĩ ra, nhưng tôi sẽ nghiên cứu phát triển đa nền tảng như một khởi đầu.
  4. Viện một số quản trị mạnh mẽ. Đó là chìa khóa để đảm bảo các tiêu chuẩn mã hóa của bạn được tuân theo. Theo các tiêu chuẩn đó là cách duy nhất để giữ cho mọi thứ có thể quản lý và duy trì. Chúng tôi đã chia sẻ những lời cầu xin không thể bỏ qua để phá vỡ mô hình mà chúng tôi đang theo dõi, nhưng không ai trong số những lời kêu gọi đó đã làm lung lay toàn bộ đội ngũ phát triển cao cấp.

Cảm ơn vì đã chia sẽ kinh nghiệm của bạn. Chỉ cần hiểu rõ hơn: Làm thế nào là một nền tảng được xác định trong trường hợp của bạn. Windows, Linux, X86 / x64? Hoặc một cái gì đó liên quan nhiều hơn đến khách hàng / môi trường khác nhau? Bạn đang làm cho một điểm tốt trong 4) Tôi nghĩ đó là một trong những vấn đề chúng ta có. Chúng tôi có một đội ngũ rất nhiều người rất thông minh và có trình độ, nhưng mọi người đều có những ý tưởng hơi khác nhau về cách làm việc. Nếu không có ai đó phụ trách kiến ​​trúc rõ ràng, thật khó để đồng ý về một chiến lược chung và bạn có nguy cơ đánh mất chính mình trong các cuộc thảo luận mà không thay đổi bất cứ điều gì.
aKzenT

@aKzenT - vâng, ý tôi là phần cứng và hệ điều hành vật lý là nền tảng. Chúng tôi đã có một bộ tính năng lớn, một số trong đó có thể lựa chọn bởi một mô-đun cấp phép. Và chúng tôi hỗ trợ một loạt các phần cứng. Liên quan đến điều đó, chúng tôi đã có một thư mục lớp chung có một loạt các thiết bị với các thư mục riêng của chúng trong lớp đó cho cây mã. Vì vậy, hoàn cảnh của chúng tôi thực sự không ở xa nơi bạn đang ở. Các nhà phát triển cấp cao của chúng tôi sẽ có những cuộc tranh luận sôi nổi với nhau, nhưng một khi quyết định tập thể được đưa ra, mọi người đều đồng ý.

4

Tôi đã làm việc nhiều năm trong một ứng dụng Quản lý lương hưu có vấn đề tương tự. Các kế hoạch hưu trí rất khác nhau giữa các công ty và đòi hỏi kiến ​​thức chuyên môn cao để thực hiện logic và báo cáo tính toán và thiết kế dữ liệu rất khác nhau. Tôi chỉ có thể đưa ra một mô tả ngắn gọn về một phần của kiến ​​trúc, nhưng có lẽ nó sẽ cung cấp đủ ý tưởng.

Chúng tôi có 2 nhóm riêng biệt: nhóm phát triển cốt lõi , chịu trách nhiệm về mã hệ thống cốt lõi (sẽ là mã được chia sẻ 80% của bạn ở trên) và nhóm thực hiện , có chuyên môn về hệ thống hưu trí và chịu trách nhiệm tìm hiểu khách hàng yêu cầu và kịch bản mã hóa và báo cáo cho khách hàng.

Chúng tôi đã có tất cả các bảng được xác định bởi Xml (điều này trước thời điểm các khung thực thể được kiểm tra theo thời gian và phổ biến). Nhóm thực hiện sẽ thiết kế tất cả các bảng trong Xml và ứng dụng cốt lõi có thể được nhắc để tạo tất cả các bảng trong Xml. Ngoài ra còn có các tệp script VB liên quan, Báo cáo tinh thể, tài liệu Word, v.v. cho mỗi máy khách. (Ngoài ra còn có một mô hình thừa kế được tích hợp trong Xml để cho phép tái sử dụng các triển khai khác).

Ứng dụng lõi (một ứng dụng cho tất cả các máy khách), sẽ lưu trữ tất cả nội dung cụ thể của máy khách khi có yêu cầu cho máy khách đó và nó tạo ra một đối tượng dữ liệu chung (giống như một bộ bản ghi ADO từ xa), có thể được nối tiếp và chuyển qua xung quanh.

Mô hình dữ liệu này ít trơn tru hơn các đối tượng thực thể / miền, nhưng nó rất linh hoạt, phổ quát và có thể được xử lý bằng một bộ mã lõi. Có lẽ trong trường hợp của bạn, bạn có thể xác định các đối tượng thực thể cơ sở chỉ bằng các trường chung và có một Từ điển bổ sung cho các trường tùy chỉnh (thêm một số loại mô tả dữ liệu vào đối tượng thực thể của bạn để nó có dữ liệu meta cho các trường tùy chỉnh. )

Chúng tôi đã có các kho lưu trữ nguồn riêng biệt cho mã hệ thống cốt lõi và mã triển khai.

Hệ thống cốt lõi của chúng tôi thực sự có rất ít logic kinh doanh, ngoài một số mô-đun tính toán chung rất chuẩn. Hệ thống cốt lõi có chức năng như: trình tạo màn hình, trình chạy tập lệnh, trình tạo báo cáo, truy cập dữ liệu và lớp vận chuyển.

Phân đoạn logic cốt lõi và logic tùy chỉnh là một thách thức khó khăn. Tuy nhiên, chúng tôi luôn cảm thấy tốt hơn là có một hệ thống cốt lõi chạy nhiều máy khách, thay vì nhiều bản sao của hệ thống đang chạy cho mỗi máy khách.


Cảm ơn vì bạn đã phản hồi. Tôi thích ý tưởng có thêm các trường trong từ điển. Điều này sẽ cho phép chúng ta có một định nghĩa duy nhất về một thực thể và đưa tất cả nội dung cụ thể của khách hàng vào từ điển. Mặc dù tôi không chắc chắn nếu có một cách tốt để làm cho nó hoạt động với trình bao bọc ORM (Entity Framework) của chúng tôi. Và tôi cũng không chắc có thực sự là một mô hình dữ liệu được chia sẻ toàn cầu hay không thay vì có một mô hình cho mọi tính năng / mô-đun.
aKzenT

2

Tôi đã làm việc trên một hệ thống nhỏ hơn (20 kloc) và thấy rằng DI và cấu hình đều là những cách tuyệt vời để quản lý sự khác biệt giữa các máy khách, nhưng không đủ để tránh làm hỏng hệ thống. Cơ sở dữ liệu được phân chia giữa một phần cụ thể của ứng dụng, có lược đồ cố định và phần phụ thuộc máy khách, được xác định thông qua tài liệu cấu hình XML tùy chỉnh.

Chúng tôi đã giữ một chi nhánh duy nhất được cấu hình như thể nó có thể giao được, nhưng được gắn nhãn hiệu và được định cấu hình cho một khách hàng hư cấu. Sửa lỗi được đưa vào dự án đó và sự phát triển mới của chức năng cốt lõi chỉ xảy ra ở đó. Các bản phát hành cho khách hàng thực tế là các nhánh ngoài đó, được lưu trữ trong kho riêng của họ. Chúng tôi theo dõi các thay đổi lớn đối với mã thông qua số phiên bản được viết thủ công và theo dõi sửa lỗi bằng cách sử dụng số cam kết.


bạn đã phân biệt giữa các thư viện cốt lõi giống nhau giữa các khách hàng hay bạn rẽ nhánh cây hoàn chỉnh? Bạn có hợp nhất quy định từ dòng chính đến các dĩa cá nhân? Và nếu có, chi phí thường xuyên hợp nhất hàng ngày mất bao nhiêu thời gian và làm thế nào bạn tránh được thủ tục này sụp đổ khi áp lực thời gian bắt đầu (giống như nó luôn xảy ra tại một thời điểm trong dự án)? Xin lỗi vì rất nhiều câu hỏi: p
aKzenT

Chúng tôi rẽ nhánh toàn bộ cây, chủ yếu là do các yêu cầu của khách hàng đến trước khi xây dựng tính toàn vẹn của hệ thống. Việc hợp nhất từ ​​hệ thống chính diễn ra thủ công bằng cách sử dụng các công cụ đồng bóng và các công cụ khác, và thường bị giới hạn ở các lỗi nghiêm trọng hoặc cập nhật tính năng lớn. Chúng tôi cố gắng cập nhật các khối không thường xuyên, cả vì chi phí hợp nhất và vì nhiều khách hàng lưu trữ hệ thống của riêng họ và chúng tôi không muốn đặt chi phí cài đặt lên chúng mà không cung cấp giá trị.
Dan Monego

Tôi tò mò: IIRC mercurial là một DVCS tương tự như git. Bạn có nhận thấy bất kỳ lợi thế nào khi thực hiện các sự hợp nhất này giữa các nhánh so với Subversion hoặc các VCS truyền thống khác không? Tôi vừa hoàn thành một quá trình hợp nhất rất đau đớn giữa 2 nhánh được phát triển hoàn toàn riêng biệt bằng cách sử dụng lật đổ và nghĩ rằng liệu có dễ dàng hơn nếu chúng ta sử dụng git.
aKzenT

Việc hợp nhất với đồng bóng đã dễ dàng hơn nhiều so với việc hợp nhất với công cụ trước đây của chúng tôi, đó là Vault. Một trong những lợi thế chính là tính đồng bóng thực sự tốt trong việc đưa mặt trận và trung tâm lịch sử thay đổi vào ứng dụng, giúp bạn dễ dàng theo dõi những gì đã được thực hiện ở đâu. Phần khó nhất đối với chúng tôi là chuyển các nhánh hiện có - nếu bạn hợp nhất hai nhánh, yêu cầu cả hai đều có cùng một gốc, do đó, việc thiết lập đó đòi hỏi phải hợp nhất hoàn toàn thủ công.
Dan Monego

2

Tôi sợ rằng tôi không có kinh nghiệm trực tiếp về vấn đề mà bạn mô tả, nhưng tôi có một số ý kiến.

Tùy chọn thứ hai, đưa mã vào cùng một kho lưu trữ trung tâm (càng nhiều càng tốt) và kiến ​​trúc để tùy chỉnh (một lần nữa, càng nhiều càng tốt) gần như chắc chắn là cách để đi trong dài hạn.

Vấn đề là làm thế nào bạn có kế hoạch để đạt được điều đó, và nó sẽ mất bao lâu.

Trong tình huống này, có thể OK (tạm thời) có nhiều bản sao của ứng dụng trong kho lưu trữ cùng một lúc.

Điều này sẽ cho phép bạn dần dần di chuyển đến một kiến ​​trúc hỗ trợ trực tiếp tùy chỉnh mà không cần phải thực hiện trong một cú trượt.


2

Cách tiếp cận thứ hai có vẻ thanh lịch hơn, nhưng chúng ta có nhiều vấn đề chưa được giải quyết trong phương pháp này.

Tôi chắc chắn rằng bất kỳ vấn đề nào trong số đó có thể được giải quyết, hết lần này đến lần khác. Nếu bạn gặp khó khăn, hãy hỏi ở đây hoặc trên SO về vấn đề cụ thể.

Như những người khác đã chỉ ra, có một kho lưu trữ mã cơ sở / một kho lưu trữ là tùy chọn bạn nên chọn. Tôi cố gắng trả lời câu hỏi ví dụ của bạn.

Ví dụ: làm thế nào để xử lý các thay đổi / bổ sung trong mô hình / cơ sở dữ liệu của bạn. Chúng tôi đang sử dụng .NET với Entity Framework để có các thực thể gõ mạnh. Tôi không thấy cách chúng tôi có thể xử lý các thuộc tính cần thiết cho một khách hàng nhưng vô dụng đối với một khách hàng khác mà không làm lộn xộn mô hình dữ liệu của chúng tôi.

Có một số khả năng, tất cả chúng tôi đã thấy trong các hệ thống trong thế giới thực. Chọn cái nào tùy thuộc vào tình huống của bạn:

  • sống với sự bừa bộn ở một mức độ nhất định
  • giới thiệu một bảng "CustomAttribution" (mô tả tên và loại) và "CustomAttributionValues" (ví dụ: đối với các giá trị được lưu trữ dưới dạng biểu diễn chuỗi, ngay cả khi chúng là số). Điều đó sẽ cho phép thêm các thuộc tính như vậy khi cài đặt hoặc thời gian chạy, có các giá trị riêng cho từng khách hàng. Đừng khăng khăng đòi mỗi thuộc tính tùy chỉnh được mô hình hóa "rõ ràng" trong mô hình dữ liệu của bạn.

  • Bây giờ, rõ ràng làm thế nào để sử dụng mã đó: chỉ có mã chung để truy cập vào các bảng đó và mã riêng lẻ (có lẽ trong một DLL trình cắm riêng biệt, tùy thuộc vào bạn) để diễn giải chính xác các thuộc tính đó

  • một cách khác là cung cấp cho mỗi bảng thực thể một trường chuỗi lớn nơi bạn có thể thêm một chuỗi XML riêng lẻ.
  • cố gắng khái quát hóa một số khái niệm, để chúng có thể được tái sử dụng dễ dàng hơn giữa các khách hàng khác nhau. Tôi đề nghị cuốn sách "Các mẫu phân tích " của Martin Fowler . Mặc dù cuốn sách này không phải là về việc tùy chỉnh phần mềm trước, nhưng nó cũng có thể hữu ích cho bạn.

Và đối với mã cụ thể: bạn cũng có thể thử giới thiệu một ngôn ngữ kịch bản vào sản phẩm của mình, đặc biệt là để thêm các tập lệnh dành riêng cho khách hàng. Bằng cách đó, bạn không chỉ tạo ra một ranh giới rõ ràng giữa mã của mình và mã dành riêng cho khách hàng, bạn cũng có thể cho phép khách hàng của mình tự tùy chỉnh hệ thống ở một mức độ nào đó.


Các vấn đề tôi thấy khi thêm các cột CustomAttribution hoặc XML để lưu trữ các thuộc tính tùy chỉnh là chúng rất hạn chế về khả năng của chúng. Ví dụ, thực hiện sắp xếp, nhóm hoặc lọc dựa trên các thuộc tính này là rất khó khăn. Tôi đã từng làm việc trên một hệ thống sử dụng bảng thuộc tính bổ sung và nó ngày càng phức tạp hơn để duy trì và xử lý các thuộc tính tùy chỉnh này. Vì lý do này, tôi đã suy nghĩ thay vì đặt các thuộc tính này dưới dạng các cột trong một bảng bổ sung được ánh xạ 1: 1 thành bản gốc. Vấn đề về cách xác định, truy vấn và quản lý chúng vẫn giống nhau.
aKzenT

@aKzenT: Khả năng tùy biến không miễn phí, bạn sẽ phải đánh đổi sự dễ sử dụng so với khả năng tùy biến. Đề nghị chung của tôi là bạn không giới thiệu các phụ thuộc trong đó phần cốt lõi của hệ thống phụ thuộc vào bất kỳ cách nào từ bất kỳ phần tùy chỉnh nào, chỉ theo cách khác. Ví dụ: khi giới thiệu các "bảng bổ sung" này cho khách hàng 1, bạn có thể tránh triển khai các bảng đó và mã liên quan cho khách hàng 2 không? Nếu câu trả lời là có, thì giải pháp là ok.
Doc Brown

0

Tôi chỉ xây dựng một ứng dụng như vậy. Tôi muốn nói rằng 90% các đơn vị được bán đã được bán, không có sửa đổi. Mỗi khách hàng có làn da tùy chỉnh của riêng họ và chúng tôi phục vụ hệ thống trong làn da đó. Khi một mod xuất hiện đã ảnh hưởng đến các phần cốt lõi, chúng tôi đã thử sử dụng phân nhánh IF . Khi mod # 2 xuất hiện cho cùng một phần, chúng tôi đã chuyển sang logic CASE được phép mở rộng trong tương lai. Điều này dường như để xử lý hầu hết các yêu cầu nhỏ.

Bất kỳ yêu cầu tùy chỉnh nhỏ nào khác đã được xử lý bằng cách triển khai Case logic.

Nếu các mod là hai gốc, chúng tôi đã tạo một bản sao (bao gồm riêng) và bọc một CASE xung quanh nó để bao gồm các mô-đun khác nhau.

Sửa lỗi và sửa đổi trên lõi ảnh hưởng đến tất cả người dùng. Chúng tôi đã thử nghiệm kỹ lưỡng trong phát triển trước khi đi vào sản xuất. Chúng tôi luôn gửi thông báo qua email kèm theo bất kỳ thay đổi nào và KHÔNG BAO GIỜ, KHÔNG BAO GIỜ, KHÔNG BAO GIỜ đăng các thay đổi sản xuất vào Thứ Sáu ... KHÔNG BAO GIỜ.

Môi trường của chúng tôi là Classic ASP và SQL Server. Chúng tôi KHÔNG phải là một cửa hàng mã spaghetti ... Mọi thứ đều được mô đun hóa bằng Bao gồm, Chương trình con và Hàm.


-1

Khi tôi được yêu cầu bắt đầu phát triển B đang chia sẻ 80% chức năng với A, tôi sẽ:

  1. Nhân bản A và sửa đổi nó.
  2. Trích xuất chức năng mà cả A và B chia sẻ vào C mà chúng sẽ sử dụng.
  3. Tạo A đủ cấu hình để đáp ứng nhu cầu của cả B và chính nó (do đó B được nhúng trong A).

Bạn đã chọn 1 và có vẻ như nó không phù hợp với hoàn cảnh của bạn. Nhiệm vụ của bạn là dự đoán cái nào trong số 2 và 3 là phù hợp hơn.


1
Nghe có vẻ dễ, nhưng làm thế nào để làm điều này trong thực tế? Làm thế nào để bạn làm cho phần mềm của bạn có thể cấu hình được mà không làm lộn xộn nó với "if (customer1)", điều này trở nên không thể nhận ra sau một thời gian.
aKzenT

@aKzenT Đó là lý do tôi để lại 2 và 3 cho bạn lựa chọn. Nếu loại thay đổi cần thiết để làm cho dự án của khách
hàng1

Tôi thích làm "if (tùy chọn1)" thay vì "if (customer1)". Bằng cách này với N tùy chọn tôi có thể quản lý rất nhiều khách hàng có thể. Ví dụ: với N tùy chọn boolean, bạn có thể quản lý 2 ^ N khách hàng, chỉ có N 'nếu' ... không thể quản lý bằng chiến lược "if (customer1)" sẽ yêu cầu 2 ^ N 'if'.
Fil
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.