Đã sáu tháng kể từ khi tôi bắt đầu làm việc trong một công ty xây dựng ứng dụng doanh nghiệp mới. Nhóm mới của tôi tuân theo một số phương pháp thực hành nhanh như lập trình cặp và Phát triển theo hướng thử nghiệm (TDD), và tôi có cảm giác trung thực rằng mặt trời đang chiếu sáng cho chúng tôi!
Chà, gần như vậy.
Có một số vấn đề mà tôi đã phải đối mặt cả bây giờ và trong kinh nghiệm công nghiệp trong quá khứ về chủ đề xây dựng các ứng dụng doanh nghiệp vững chắc mà tôi muốn giải thích cho bạn trong bài viết này.
Hơn nữa, tôi cũng sẽ đề xuất một phương pháp dựa trên thử nghiệm đơn giản dựa trên thử nghiệm đầu tiên để xây dựng các ứng dụng doanh nghiệp nhằm nâng cao khả năng giao tiếp của nhóm và thúc đẩy việc phân phối mã chất lượng cao nhanh hơn.
Không cần thêm lời khuyên, chúng ta hãy bắt đầu!
Thực hành Agile rất có lợi cho việc tạo mẫu phần mềm nhanh chóng. TDD là cốt lõi của các hoạt động như vậy, cung cấp cho phần mềm một đặc tính tối quan trọng: tính mạnh mẽ. Việc viết các bài kiểm tra từ trước buộc các nhà phát triển phải suy nghĩ về hành vi dự kiến và đặc biệt của các thành phần phần mềm mà họ xây dựng, nâng cao chất lượng mã và đảm bảo đáp ứng các yêu cầu chức năng.
Hơn nữa, TDD là một thực tiễn mạnh mẽ cho phép các nhà phát triển không ngại sửa, làm sạch và điều chỉnh mã của họ với các bản cập nhật yêu cầu chức năng . Và đó là tất cả tuyệt vời. Tuy nhiên, để đưa TDD vào tập luyện không phải là điều dễ dàng.
Khi bắt đầu xây dựng bất kỳ ứng dụng doanh nghiệp mới nào, chúng tôi (các nhà phát triển) sẽ thu thập một số yêu cầu chức năng. Sau đó, chúng tôi suy ra một tập hợp các trường hợp sử dụng sẽ thỏa mãn các yêu cầu chức năng đó. Sau đó, chúng tôi phát triển các thành phần phần mềm ở các lớp khác nhau, từ cao nhất đến thấp nhất, đi sâu xuống trung tâm của ứng dụng, tức là mô hình miền của nó. Đó là định nghĩa của quy trình phát triển phần mềm từ trên xuống .
Tuy nhiên, quy trình phát triển phần mềm từ dưới lên phù hợp hơn với TDD. So với giải pháp thay thế từ trên xuống, từ dưới lên là một cách tiếp cận thực dụng hơn, vì nó cho phép chúng tôi, các nhà phát triển, từng bước bao phủ tất cả các cấp độ chuyển hướng bắt đầu với cơ bản nhất (tức là mô hình miền) và dần dần tiến tới các lớp cao hơn của sự trừu tượng. Nó cho phép tạo ra các nền tảng mã ứng dụng tốt hơn, do đó làm cho chúng tôi có được sự tự tin lớn trong công việc của mình. Tuy nhiên, để áp dụng TDD theo cách tiếp cận từ trên xuống, trước tiên người ta nên viết các bài kiểm tra cho các thành phần phần mềm nằm ở các lớp cao hơn. Bằng cách này, các nhà phát triển không cần phải bắt chước bất kỳ sự phụ thuộc nào vào bất kỳ thành phần lớp thấp hơn nào, vì chúng chỉ đơn giản là chưa tồn tại.
Nhu cầu tạo ra các phụ thuộc như vậy đã là một vấn đề vì không phải lúc nào cũng có thể bắt chước các thành phần lớp thấp hơn hoặc trong trường hợp tốt nhất, cảm thấy phản trực giác, ví dụ, hãy tưởng tượng bạn phải giả lập logic của một đối tượng miền để tạo ra một kiểm tra thành phần dịch vụ.
Hơn nữa, cá nhân tôi nghi ngờ rằng nó sẽ mang lại bất kỳ giá trị nào, vì tôi nghĩ rằng xác thực thành phần trung gian phải luôn thực hiện tất cả các phụ thuộc ngoại trừ các phụ thuộc đối với các dịch vụ bên ngoài (ví dụ: cơ sở dữ liệu), có thể bị giả mạo. Quan trọng hơn, do sự phức tạp cố hữu để thực hiện các yêu cầu chức năng không tầm thường như mã, người ta không hiểu đầy đủ các hàm ý kỹ thuật mà một số yêu cầu chức năng nhất định có đối với mô hình miền cho đến khi bắt đầu lý luận về chúng trong quá trình thực hiện mô hình miền.
Một lần nữa, việc bắt đầu viết các bài kiểm tra của các thành phần phần mềm trung gian không mang lại nhiều giá trị, vì nhiều bài kiểm tra trong số đó (nếu không phải là tất cả) có khả năng bị ném vào thùng rác một khi các thành phần phần mềm lớp dưới thực sự được triển khai.
Hơn nữa, tôi đã thấy các nhà phát triển phần mềm (đặc biệt là các đồng đội cấp dưới) từ bỏ và triển khai một số bằng chứng về khái niệm cho trường hợp sử dụng mà không viết bất kỳ logic xác thực nào. Điều này đang sử dụng thực hành mã đầu tiên, điều này đánh bại mục đích của TDD. Ngoài ra, nếu không tuân theo các phương pháp Phân phối liên tục thích hợp, sẽ có nhiều rủi ro dẫn đến việc đẩy mã không được xác thực vào kho lưu trữ kiểm soát phiên bản của chúng tôi.
Vì vậy, làm thế nào chúng ta có thể áp dụng hiệu quả TDD trong việc sản xuất các ứng dụng doanh nghiệp với một tập hợp các yêu cầu chức năng?
Có rất nhiều tài liệu về Kiến trúc Lục giác trên Internet. Tôi đặc biệt khuyên bạn nên đọc sách trắng về chủ đề do Alistair Cockburn viết.
Với mục đích của bài viết này, xin phép tôi kể cho các bạn nghe một câu chuyện ngắn có thật nhằm giải thích ngắn gọn về động lực và lợi ích chính của Kiến trúc Lục giác: Trong nhiều năm tôi phát triển các ứng dụng doanh nghiệp, tôi đã thấy nhiều người ( bao gồm cả bản thân tôi) bắt đầu các dự án mới tập trung vào các chủ đề khác hơn là trong sứ mệnh thực sự của chúng tôi. Sứ mệnh như vậy bao gồm việc cung cấp giá trị thực tế cho các công ty mà chúng tôi đang làm việc. Giá trị đó nằm trên logic miền của các ứng dụng của chúng tôi.
Diễn giải chú Bob trong cuốn sách Clean Architecture của mình, mọi thứ khác đều là sự phân tâm, một chi tiết triển khai có thể (và nên) bị trì hoãn, lý tưởng là cho đến cuối quá trình phát triển. Ví dụ về chi tiết triển khai là công nghệ cơ sở dữ liệu, logic bộ điều khiển hoặc công nghệ giao diện người dùng. Ngay cả khung phụ trợ cũng là một chi tiết triển khai mà chúng tôi có thể chọn sau này trong quá trình phát triển nếu chúng tôi thực sự muốn. Kiến trúc Lục giác, còn được gọi là Cổng và Bộ điều hợp , là một mô hình kiến trúc nhằm tách logic cốt lõi của ứng dụng phần mềm khỏi các chi tiết triển khai bên ngoài.
Các nhà phát triển của chúng tôi nên tập trung vào logic cốt lõi của các ứng dụng doanh nghiệp và hoãn việc triển khai logic cần thiết để giao tiếp với các dịch vụ bên ngoài. Để đạt được mục tiêu đó, tất cả những gì chúng ta thực sự cần làm là viết một số giao diện (cái gọi là cổng ) và mô phỏng các thành phần (cái gọi là bộ điều hợp ) thực sự giao tiếp với các dịch vụ bên ngoài. Do đó, việc triển khai bộ điều hợp có thể đến muộn hơn trong quá trình phát triển. Và chúng đến càng muộn càng tốt, vì tất cả những hiểu biết sâu sắc mà chúng ta có được trong khi tạo ra logic cốt lõi đều thực sự hữu ích trong quá trình ra quyết định chọn công nghệ nào.
Hình ảnh sau đây là một trong nhiều hình ảnh minh họa hiện có của Kiến trúc Lục giác:
Xem xét các yếu tố tạo nên hình lục giác bên trong. Ngoài mô hình miền, còn có một lớp các dịch vụ ứng dụng . Các thành phần phần mềm này không chỉ định bất kỳ logic miền nào. Thay vào đó, chúng là những bộ điều phối đơn giản của logic mô hình miền và bộ điều hợp. Một dịch vụ ứng dụng nhận ra một ca sử dụng xử lý một tập hợp con các yêu cầu chức năng cho ứng dụng doanh nghiệp. Đây là dữ liệu quan trọng cần ghi nhớ cho những gì xảy ra tiếp theo.
Như tôi đã trình bày trước đó, việc áp dụng TDD dễ dàng hơn khi tuân theo quy trình phát triển phần mềm từ dưới lên. Tuy nhiên, nhiều người trong chúng ta thấy dễ dàng hơn khi lập luận về thiết kế hệ thống theo cách tiếp cận từ trên xuống. Và mặc dù có vẻ như chúng ta đang phải chịu xung đột lợi ích, điều đó vẫn ổn vì chúng ta có thể bắt đầu thiết kế (ví dụ: phác thảo bằng mã giả hoặc một số sơ đồ UML) các dịch vụ ứng dụng của chúng ta từ trên xuống mà không cần viết một dòng mã nào cho chúng; không cho đến khi chúng tôi hoàn thành việc triển khai mô hình miền.
Trước khi bắt đầu viết mã, chúng ta có thể hiểu các dịch vụ ứng dụng như một số hướng dẫn thiết kế phần mềm để thực hiện các lát dọc của các ứng dụng doanh nghiệp mà chúng ta sẽ xây dựng. Mỗi lát dọc đại diện cho toàn bộ đường dẫn thực thi của một ca sử dụng, từ hành động được thực hiện bởi một dịch vụ bên ngoài ngược dòng hoặc một người dùng trong giao diện người dùng đến bất kỳ hoạt động nào được thực hiện trên một dịch vụ bên ngoài hạ lưu. Vào thời điểm hoàn thành việc thiết kế một dịch vụ ứng dụng, chúng tôi đã xác định được bộ điều hợp và thành phần miền nào chúng tôi cần triển khai. Bây giờ chúng ta đã có khả năng hiển thị trên mô hình miền, tiếp theo chúng ta có thể triển khai các thành phần cơ bản của nó bằng cách áp dụng TDD.
Sau đó, chúng ta có thể triển khai dịch vụ ứng dụng theo phương pháp thử nghiệm đầu tiên, tạo một cổng tới bất kỳ bộ điều hợp dịch vụ bên ngoài nào và bắt chước việc triển khai thực tế của nó. Vào thời điểm chúng tôi hoàn thành việc triển khai dịch vụ ứng dụng và mô hình miền liên quan, chúng tôi có thể chắc chắn rằng việc triển khai đó là hợp lệ, tức là không có lỗi và phù hợp với các yêu cầu chức năng của nó. Cuối cùng, chúng ta có thể triển khai logic bộ điều hợp, cũng áp dụng TDD.
Phương pháp luận này cho phép triển khai nhanh chóng và dần dần các ứng dụng doanh nghiệp, cho phép các nhà phát triển tin tưởng vào tính hợp lệ của tất cả các thành phần mà họ tạo ra mà không phải bỏ qua bất kỳ thử nghiệm nào. Hơn nữa, nó không áp đặt bất kỳ hạn chế nào đối với các bản cập nhật yêu cầu chức năng.
Sơ đồ quy trình sau đây mô tả phương pháp luận để viết logic của một ca sử dụng. Lưu ý rằng tôi không nói về các loại bài kiểm tra cụ thể vì đây là một chủ đề gây tranh cãi khá nhiều, mặc dù tôi khuyên bạn nên tuân theo các quy ước và thuật ngữ được sử dụng trong bài viết The Practical Test Pyramid .
Phương pháp được đề xuất đơn giản hóa việc phân phối nhiệm vụ của nhóm, bất kể họ làm việc một mình hay theo cặp. Một số bước của nó có thể được thực hiện song song, ví dụ: một đồng đội / cặp có thể xây dựng logic cốt lõi mô phỏng bất kỳ tham chiếu nào đến phần phụ thuộc bên ngoài, trong khi một người khác có thể làm việc trên sự phát triển của mã tích hợp với phần phụ thuộc bên ngoài như vậy, vì hai phần này là tách rời, do đó rút ngắn thời gian giao hàng. Tất cả những gì họ cần làm là chuyển tải một số API cho sự phụ thuộc bên ngoài được thực hiện như một cổng. Cả mô hình và phần phụ thuộc bên ngoài thực tế đều cần triển khai giao diện cổng đã nói ở trên. Tương tự, một nhóm / đồng đội / cặp khác có thể triển khai phần giao diện người dùng cho trường hợp sử dụng, nếu đó là những gì ứng dụng doanh nghiệp yêu cầu.
Mặt khác, do tính chất có cấu trúc của nó, việc truyền đạt trạng thái phát triển của một ca sử dụng cụ thể giữa các đồng nghiệp rất dễ dàng. Khi chuyển giao sự phát triển ca sử dụng, thực thể khởi hành có thể chỉ cần hướng cái mới đến dịch vụ ứng dụng hiện tại mà họ đang thiết kế hoặc triển khai. Thực thể mới sau đó có thể đơn giản theo dõi bất kỳ thứ gì đã được tạo trên bộ điều hợp hoặc lôgic miền trong cơ sở mã.
Lưu ý cuối cùng về chi tiết triển khai: Chúng tôi có thể cập nhật mô hình miền của mình để chỉ định một số chi tiết đó bằng cách viết chú thích / trang trí của công nghệ cơ sở dữ liệu và đầu vào tài nguyên xác thực dữ liệu từ khung cơ bản điều chỉnh tốt nhất cho bản chất ứng dụng của chúng tôi. Tuy nhiên, tôi khuyên bạn không nên làm điều đó, vì điều đó sẽ làm rò rỉ chi tiết triển khai vào mô hình miền của chúng tôi, đây không phải là phương pháp hay nhất vì chi tiết triển khai và mô hình miền có xu hướng thay đổi vì các lý do và tần suất khác nhau. Mặt khác, như được giải thích bởi Vaughn Vernon trong cuốn sách Triển khai thiết kế theo hướng miền (DDD), Kiến trúc lục giác có liên quan chặt chẽ với DDD. Tuy nhiên, bạn không cần phải tuân theo tập hợp các mẫu và thực hành DDD để xây dựng các ứng dụng doanh nghiệp phức tạp dựa trên Kiến trúc lục giác, mặc dù tôi thực sự khuyên bạn nên làm như vậy. Nhưng suy cho cùng, những quyết định đó hoàn toàn phụ thuộc vào bạn.
Phát triển theo hướng thử nghiệm là một phương pháp hữu hiệu trong việc xây dựng các ứng dụng doanh nghiệp mạnh mẽ. TDD được áp dụng tốt nhất theo quy trình phát triển phần mềm từ dưới lên. Tuy nhiên, vì các nhà phát triển đã quen với việc lập luận về các ứng dụng như vậy theo cách tiếp cận từ trên xuống nên việc áp dụng nó vào thực tế là một thách thức. Bài viết này chỉ ra một phương pháp đơn giản để giúp các nhà phát triển áp dụng hiệu quả TDD trong việc phát triển các ứng dụng doanh nghiệp.