Kiến trúc tiến hóa và thiết kế nổi dần: Nghiên cứu điều tra kiến trúc và thiết kế

Khám phá kiến trúc và thiết kế dễ bảo trì hơn

Thiết kế và kiến trúc phần mềm phát sinh ra nhiều sức nóng tranh luận nhưng không phát sinh nhiều ánh sáng. Để bắt đầu một cuộc trò chuyện mới về những cách nghĩ khác đi về chúng, bài viết này khởi đầu loạt bài Kiến trúc tiến hóa và thiết kế nổi dần. Kiến trúc tiến hóa và thiết kế nổi dần (Evolutionary architecture and emergent design) là các kỹ thuật khéo léo để lùi các quyết định quan trọng cho đến thời điểm hợp lý cuối cùng. Trong bài giới thiệu này, tác giả Neal Ford của loạt bài viết sẽ định nghĩa kiến trúc và thiết kế rồi sau đó xác định các mối quan tâm bao quát sẽ nảy sinh trong suốt cả loạt bài viết này.

Neal Ford, Kiến trúc phần mềm, ThoughtWorks

Neal Ford là một kiến trúc sư phần mềm và Meme Wrangler tại Thought Works, một văn phòng tư vấn CNTT toàn cầu. Ông cũng thiết kế và phát triển các ứng dụng, tài liệu hướng dẫn, các bài báo trên tạp chí, học liệu và các bài thuyết trình video/DVD; và ông là tác giả hoặc người biên tập các cuốn sách bao trùm nhiều loại công nghệ, bao gồm cả cuốn sách gần đây nhất là The Productive Programmer. Ông tập trung vào việc thiết kế và xây dựng ứng dụng doanh nghiệp có quy mô lớn. Ông cũng là một diễn giả được quốc tế hoan nghênh tại hội nghị của các nhà phát triển trên toàn thế giới



20 05 2009

Kiến trúc và thiết kế phần mềm đã chống lại mọi định nghĩa vững chắc trong một thời gian dài do việc phát triển phần mềm, như một lĩnh vực chuyên môn, vẫn còn chưa được nắm bắt đầy đủ tất cả những rắc rối và các mối liên quan của chúng. Nhưng để tạo ra một cuộc đàm luận đúng mức về những chủ đề này, bạn phải bắt đầu từ đâu đó. Loạt bài viết này quan tâm đến kiến trúc tiến hóa và thiết kế nổi dần, do đó cần mở đầu cho loạt bài viết này bằng một loạt các định nghĩa, xem xét cân nhắc và thiết lập nền tảng khác.

Về loạt bài viết này

Loạt bài này nhằm mục đích cung cấp một cách nhìn mới mẻ về các khái niệm thường được bàn luận nhưng khó nắm bắt ý nghĩa, đó là thiết kế và kiến trúc phần mềm. Thông qua các ví dụ cụ thể, Neal Ford sẽ mang lại cho bạn một nền móng vững chắc về các biện pháp thực hành nhanh trong kiến trúc tiến hóathiết kế nổi dần. Bằng cách lùi các quyết định kiến trúc và thiết kế quan trọng đến thời điểm hợp lý cuối cùng, bạn có thể ngăn ngừa không để những phức tạp không cần thiết hủy hoại các dự án phần mềm của bạn.

Định nghĩa kiến trúc

Kiến trúc trong phần mềm là một trong những khái niệm được nói đến nhiều nhất nhưng lại được hiểu ít nhất mà các nhà phát triển phải đánh vật với nó. Tại các hội nghị, các cuộc thảo luận và tụ họp của những người cùng chí hướng về kiến trúc chật cả tòa nhà, nhưng chúng ta vẫn chỉ có các định nghĩa mơ hồ về nó. Khi chúng ta thảo luận về kiến trúc, chúng ta thực sự đang nói về một số mối quan tâm khác nhau nhưng có liên quan đến nhau, thường rơi vào những phạm trù rộng rãi của kiến trúc ứng dụngkiến trúc doanh nghiệp.

Kiến trúc ứng dụng

Kiến trúc ứng dụng mô tả các mảnh ở mức chi tiết thô làm thành một ứng dụng. Ví dụ như trong thế giới Java, kiến trúc ứng dụng mô tả hai điều: sự kết hợp của các khung công tác được sử dụng để xây dựng một ứng dụng cụ thể — mà tôi gọi là kiến trúc mức khung công tác — và sự phân tách các mối quan tâm logic truyền thống hơn mà tôi luôn gắn cho biệt danh là kiến trúc ứng dụng. Việc tách kiến trúc khung công tác như một sự phân biệt là có ý nghĩa vì hầu hết các nhà thực hành ngôn ngữ hướng đối tượng đã phát hiện ra rằng các lớp riêng lẻ không làm việc tốt như là một cơ chế tái sử dụng. (Lần cuối cùng bạn đã tải về chỉ một lớp đơn từ Internet để sử dụng trong một dự án là khi nào?) Đơn vị tái sử dụng trong các ngôn ngữ hướng đối tượng hiện đại là một thư viện hay một khung công tác. Khi bạn bắt đầu một dự án mới trong các ngôn ngữ giầu khung công tác như Java, một trong các mối quan tâm về kiến trúc đầu tiên là kiến trúc mức khung công tác của ứng dụng. Phong cách tái sử dụng kiểu này thắng thế hơn hẳn trong thế giới Java đến mức mà tôi đã bắt đầu nói rằng chúng ta sẽ nên thôi không gọi lập trình Java là một ngôn ngữ hướng đối tượng mà chỉ nên gọi nó là một ngôn ngữ hướng khung công tác. Theo nhiều cách, kiến trúc mức khung công tác biểu diễn một kiến trúc vật lý được mô tả bởi các khối xây dựng nền tảng cụ thể.

Một khía cạnh thú vị khác của kiến trúc ứng dụng là mô tả cách làm thế nào để các mảnh lô gic của ứng dụng ăn khớp với nhau. Đây là lĩnh vực của các mẫu thiết kế và các mô tả cấu trúc khác và vì vậy có xu hướng trở nên vừa trừu tượng hơn vừa mang tính logic hơn là kiến trúc vật lý. Ví dụ, bạn có thể nói rằng một ứng dụng web trung thành với mẫu Mô hình -Khung nhìn -Trình bày mà không cần chỉ rõ bạn sử dụng khung công tác nào để thực hiện được sự sắp xếp lô gic đó. Sự sắp xếp lô gic này là cái có vẻ thích hợp nhất để trang trí các bảng trắng của vùng làm việc của bạn khi bắt đầu làm việc trên các phần mới của ứng dụng.

Kiến trúc doanh nghiệp

Kiến trúc doanh nghiệp bản thân nó quan tâm đến cách làm thế nào để doanh nghiệp như là một tổng thể (thường có nghĩa là các ứng dụng đang chạy bên trong một tổ chức lớn) sử dụng các ứng dụng. Một phép ẩn dụ hữu ích thường dùng về mối quan hệ giữa kiến trúc doanh nghiệp và kiến trúc ứng dụng là ví kiến trúc doanh nghiệp như việc quy hoạch thành phố và kiến trúc ứng dụng là xây dựng các công trình kiến trúc. Các nhà quy hoạch thành phố phải suy nghĩ về việc cấp nước, điện, thoát nước thải và các dịch vụ khác để cho phép thành phố hoạt động. Bạn không thể có một tòa nhà tiêu thụ nhiều nước hơn lượng nước được chia sẻ cho nó. Kiến trúc doanh nghiệp suy nghĩ về các vấn đề giống như thế nhưng cho các ứng dụng: bạn không thể cho phép một ứng dụng tiêu thụ tất cả băng thông của mạng thông tin và nếu các dịch vụ cơ sở hạ tầng bị sụp đổ, nước thải (ảo) lại dâng lên.

Kiến trúc doanh nghiệp đã được chú ý rất nhiều trong vài năm gần đây nhờ có kiến trúc hướng dịch vụ (SOA). SOA bản thân nó đã là một chủ đề lớn, do đó các bài viết sắp tới của loạt bài này sẽ đề cập đến SOA như là một trường hợp riêng. Nó cũng có những khía cạnh đáng quan tâm riêng bởi vì nó làm mờ đi đường biên giới giữa kiến trúc doanh nghiệp và kiến trúc ứng dụng khi nó áp đặt các đặc trưng kỹ thuật của cấu trúc ứng dụng.

Các đoạn trên đây đưa ra những định nghĩa hời hợt bên ngoài về các khái niệm quan trọng này, nhưng chúng sẽ là bệ phóng xuất phát cho các định nghĩa khác đáng chú ý hơn, nhiều sắc thái hơn về kiến trúc bao gồm cả một vài định nghĩa thu hoạch được từ những định nghĩa khác.

Các định nghĩa hiện có

Nhiều người thông minh đã thử định nghĩa kiến trúc phần mềm, do đó tôi sẽ để họ đưa ra một vài điều đáng suy nghĩ. Trong bài tham luận kinh điển của mình "Ai cần một kiến trúc sư?" (xem Tài nguyên), Martin Fowler bàn luận về một số định nghĩa. Ông trích dẫn định nghĩa đầu tiên từ một bài đăng tải trên danh sách thư chung Lập trình đỉnh cao (Extreme Programming mailing list):

" RUP, cố thoát khỏi định nghĩa của IEEE, định nghĩa kiến trúc như là 'quan niệm mức cao nhất về một hệ thống trong môi trường của nó. Kiến trúc của một hệ thống phần mềm (tại một thời điểm đã cho) là tổ chức hoặc cấu trúc của các thành phần quan trọng của nó, tương tác với nhau qua các giao diện; các thành phần đó đến lượt mình lại bao gồm những thành phần và giao diện nhỏ hơn.'"

Định nghĩa này vừa vặn nằm trong lĩnh vực của kiến trúc ứng dụng như tôi đã mô tả ở trên. Còn hơi mơ hồ, nhưng nó nắm bắt được bản chất của trọng trách kiến trúc: quan niệm mức cao nhất.

Fowler sau đó trích dẫn Ralph Johnson, người đã tranh luận về định nghĩa trên đây khi trả lời trên danh sách thư chung:

"Định nghĩa tốt hơn sẽ là: 'Trong hầu hết các dự án phần mềm thành công, các nhà phát triển lão luyện đang làm việc trên dự án đó chia sẻ một hiểu biết chung về thiết kế của hệ thống được thiết kế. Sự hiểu biết chung này được gọi là "kiến trúc". Sự hiểu biết này bao gồm cách thức hệ thống được chia ra thành các thành phần như thế nào và các thành phần ấy tương tác với nhau thông qua giao diện như thế nào.'"

Johnson nhấn mạnh một điểm tuyệt vời rằng việc phát triển phần mềm dựa trên trao đổi thông tin nhiều hơn là dựa trên công nghệ và kiến trúc thực sự là hiện thân của kiến thức chung về hệ thống, chứ không phải một cái gì đặc biệt về ngôn ngữ, về khung công tác hay những phù du công nghệ khác.

Trong bài báo vừa nói đến ở trên, chính Fowler cung cấp một trong những định nghĩa về kiến trúc mà tôi yêu thích:

"Kiến trúc là về một thứ quan trọng. Dù đó là bất cứ thứ gì".

Điều này đủ mơ hồ nhưng đồng thời cũng đủ diễn tả. Nhiều tranh luận về kiến trúc và thiết kế nhằm phân giải sự hiểu lầm điều gì là quan trọng. Cái quan trọng đối với các nhà phân tích nghiệp vụ khác cái quan trọng đối với kiến trúc sư doanh nghiệp. Định nghĩa này gói ghém rằng bạn phải định nghĩa bằng ngôn từ của bạn trong môi trường của bạn trước khi bạn có thể cố gắng định nghĩa các thứ khác.

Định nghĩa của Fowler cũng làm nổi bật khía cạnh quan trọng khác của việc định nghĩa một thứ nhiều sắc thái như kiến trúc. "Cái quan trọng", không chỉ khác nhau giữa các cá nhân và các nhóm; những sự khác biệt ấy trong thực tế có thể loại trừ lẫn nhau. Ví dụ, hầu như tất cả các SOA phải thực hiện một sự thỏa hiệp giữa tính linh hoạt và tốc độ. Hệ thống khách - chủ xưa cũ mà bạn hiện đang sử dụng hầu như chắc chắn sẽ nhanh hơn các phiên bản thay thế nó mà dựa trên Web, dựa trên máy portlet hay dựa vào dịch vụ. Trừ khi ứng dụng mới được các nhà phát triển rất kém viết ra, các tầng bổ sung thêm cung cấp tính linh hoạt cũng có nghĩa là thời gian đáp ứng cho những người dùng tăng lên, làm cho nó chậm hơn với người dùng. Có lẽ kiến trúc sư là người sẽ phải nói với những người sử dụng, "Ôi, nhân đây tôi muốn nói rằng, cái SOA mới mà chúng tôi vừa cài đặt sẽ làm việc tốt hơn cho chúng tôi, nhưng bây giờ thì công việc của bạn sẽ mất nhiều thì giờ hơn đấy. Xin lỗi nhé". Có lẽ đó là lý do tại sao các kiến trúc sư được trả tiền nhiều hơn các nhà phát triển.

Còn lại định nghĩa kiến trúc yêu thích của tôi như sau:

"Điều khó có thể thay đổi sau này".

Định nghĩa này phù hợp nhất với ý tưởng về một kiến trúc tiến hóa. Một trong những ý tưởng cốt lõi đằng sau kiến trúc tiến hóa là trì hoãn quyết định, càng chậm càng tốt, nó cho phép bạn chọn những cách thay thế mà các trải nghiệm vừa mới đây cho thấy là tốt hơn. Nhiều khối nền tảng của phong cách kiến trúc này xuất hiện trong suốt loạt bài này và là động cơ thúc đẩy tạo ra loạt bài viết này.

Trước khi rời cuộc thảo luận về kiến trúc, sẽ là tắc trách nếu tôi không bàn về chức danh "kiến trúc sư". Nó chọc tức các phòng nhân sự vì lẽ đã là một ngành công nghiệp mà chúng ta lại có những chức danh công việc định nghĩa mơ hồ đến như thế. Nhiều tổ chức muốn thăng cấp các nhà phát triển tốt nhất của họ — những người đưa ra các quyết định quan trọng về những điều khó có thể thay đổi sau này — nhưng không có thuật ngữ công nghiệp nào phù hợp hơn ngoài "kiến trúc sư". Cũng không có bản mô tả công việc chung nào, do đó mỗi công ty tự định nghĩa vai trò này có nghĩa là gì. Một số kiến trúc sư giống với ông Kiến trúc sư ở phần cuối của bộ phim Ma trận thứ hai (mà Fowler xếp loại là Architectus Reloadus). Các kiến trúc sư này đã viết những dòng mã cuối cùng khoảng một thập kỷ trước và bây giờ họ đang thực hiện các quyết định quan trọng đối với công ty của bạn. Công cụ phát triển phần mềm duy nhất mà họ sử dụng là Visio.

Một vai trò khác của kiến trúc sư là cái mà Fowler gọi là Architectus Oryzus ((do David Rice, một trong những đồng nghiệp của tôi đặt tên). Các kiến trúc sư này đã tích cực viết mã cho dự án, sát cánh với các nhà phát triển khác trong những phần việc khó nhất. Trách nhiệm của họ cũng bao gồm cả việc tương tác với các bên liên quan khác của dự án để đảm bảo rằng tất cả mọi người nói cùng một ngôn ngữ, sử dụng cùng các định nghĩa và hiểu rõ các phần của hệ thống mà họ cần phải hiểu được. Rõ ràng, vai trò tích cực như thế này là trọng yếu để thực hiện các mục tiêu của kiến trúc tiến hóa và vì vậy sẽ xuất hiện lặp đi lặp lại trong loạt bài này.


Định nghĩa thiết kế

Hầu hết các nhà phát triển đã có một ý niệm khá đúng về thiết kế, vì vậy tôi sẽ không dành nhiều thời gian định nghĩa nó. Nó là các đai ốc và các bu lông trong việc ghép các mảnh phần mềm lại với nhau như thế nào. Nó bao trùm toàn bộ các lĩnh vực đã được xử lý tốt như các mẫu thiết kế, tái cấu trúc, các khung công tác và các mối quan tâm hàng ngày của nhà phát triển. Đại khái, thiết kế rơi vào một phạm vi giữa BDUF (Big Design Up Front - thiết kế lớn làm trước) và Cowboy Hacking (việc cưỡi ngựa của anh cao bồi- với ý là thiết kế tự do), như được hiển thị trong Hình 1:

Hình 1. Phổ các kiểu thiết kế
Phổ các kiểu thiết kế

Phần bên trái của dải phổ trong hình 1 ám chỉ rằng bạn có thể dự đoán trước tất cả hàng trăm và hàng ngàn mối quan tâm sẽ bật lên khi bạn phát triển phần mềm và cố gắng để hạn chế câu trả lời của bạn dành cho chúng. Bạn sẽ đọc nhiều hơn về vấn đề này trong các bài viết tiếp theo. Bởi vì tôi không dành nhiều thời gian định nghĩa thiết kế không có nghĩa là tôi sẽ không dành nhiều thời gian nói chuyện về nó. Phần chính của loạt bài này trình bày các khía cạnh khác nhau về cách làm thế nào để bạn có thể cho phép thiết kế nổi dần lên khi bạn phát triển chứ không phải là khắc nó vào đá trước khi bạn viết dòng mã đầu tiên của mình.


Các mối quan tâm về kiến trúc và thiết kế

Tiến hóa so với nổi dần

Lưu ý rằng loạt bài này được đặt tên là Kiến trúctiến hóa và thiết kế nổi dần. Tại sao có sự phân biệt giữa tiến hóanổi dần? Kiến trúc nổi dần, như một trong những đồng nghiệp của tôi nói với tôi, không phải là một ý tưởng nóng hổi lắm. Nếu bạn chấp nhận tiền đề rằng kiến trúc là về những gì đó khó thay đổi sau này, thì thật là khó cho phép một kiến trúc nổi dần lên. Kiến trúc quan tâm đến các yếu tố cơ sở hạ tầng phải tồn tại trước khi bạn có thể bắt đầu ứng dụng. Tuy nhiên, chỉ vì bạn không thể cho phép kiến trúc nổi dần lên không có nghĩa là nó không thể tiến triển. Nếu bạn đã tạo ra một kiến trúc linh hoạt và đã chú ý không để tạo ra các quyết định không thể thay đổi được, thì bạn có thể cho phép nó tiến triển theo thời gian khi các mối quan tâm mới lại xuất hiện.

Bây giờ khi đã có các định nghĩa để làm việc về kiến trúc và thiết kế trong tay, tôi muốn xoáy sâu vào toàn bộ các lĩnh vực quan tâm. Tất cả những chủ đề này giao cắt với cả kiến trúc lẫn thiết kế ở cấp độ cơ bản, do đó việc trình bày trước cho phép tôi nhắc lại chúng về sau trong loạt bài này. Trước tiên, tôi bàn luận về nợ kỹ thuật (technical debt), sau đó là về độ phức tạp, và cuối cùng là về tính bao quát tràn lan.

Món nợ chính và lãi phát sinh

Mỗi nhà phát triển đều ý thức được khái niệm nợ kỹ thuật, khi bạn thỏa hiệp trong thiết kế của bạn vì một số sức ép bên ngoài, ví dụ như sức ép về lịch trình công việc. Nợ kỹ thuật giống như nợ thẻ tín dụng: bạn không có đủ tiền vào lúc này, vì vậy bạn vay nợ rồi trả lại trong tương lai. Tương tự, dự án của bạn hiện không có đủ thời gian để làm một cái gì đó đúng cách, vì vậy bạn phải dùng tạm một giải pháp vừa kịp có và hy vọng lúc nào đó trong tương lai sẽ quay lại và làm mới lại nó. Thật không may, rất nhiều nhà quản lý có vẻ không hiểu về nợ kỹ thuật, phản đối việc quay về xem xét lại công việc trong quá khứ.

Việc xây dựng phần mềm không giống như việc đào mương. Nếu bạn thỏa hiệp khi bạn đào mương, bạn chỉ bị chiều rộng không đồng đều hoặc chiều sâu không bằng phẳng. Mương có sai sót ngày hôm nay không ngăn cản bạn đào một con mương không sai sót ngày mai. Nhưng phần mềm mà bạn xây dựng hôm nay là cơ sở cho những gì bạn sẽ xây dựng vào ngày mai. Các thỏa hiệp phải làm bây giờ vì lý do thực dụng sẽ gây ra entropi tích tụ dần trong phần mềm của bạn. Trong cuốn Nhà lập trình thực dụng (Pragmatic Programmer), Andy Hunt và Dave Thomas nói về entropi trong phần mềm và tại sao nó gây hậu quả thiệt hại đến như vậy (xem Tài nguyên). Entropi là một độ đo mức phức tạp và nếu bạn tăng thêm độ phức tạp hiện nay vì một giải pháp tạm vừa kịp có cho một vấn đề, bạn phải trả một giá nào đó cho điều này trong vòng đời còn lại của dự án.

Giả dụ rằng bạn muốn thêm các tính năng mới cho một dự án dài hạn hiện có. Những tính năng mới này có một độ phức tạp vốn có nhất định kèm với chúng. Tuy nhiên, nếu bạn đã có nợ kỹ thuật rồi, bạn phải vòng tránh tất cả những phần đã thỏa hiệp trong hệ thống đó để thêm các tính năng mới. Do đó, chi phí bổ sung thêm phản ánh phần ẩn về tài chính. Hình 2 cho thấy sự khác biệt giữa các nỗ lực cần thiết để thêm một tính năng mới vào một hệ thống được thiết kế sạch (ví dụ, một hê thống có ít hoặc không có nợ kỹ thuật), so với một hệ thống điển hình có nhiều nợ kỹ thuật.

Hình 2. Nợ kỹ thuật và lãi phát sinh
Cố gắng thêm các đặc tính mới

Bạn có thể suy nghĩ về độ phức tạp vốn có như là món nợ chính và các nỗ lực phát sinh thêm gây ra bởi thủ thuật làm tắt thích hợp nêu trên như là lãi phát sinh. Chính bản thân độ phức tạp là một chủ đề thú vị.

Độ phức tạp bản chất so với độ phức tạp ngẫu nhiên

Các vấn đề mà chúng ta giải quyết trong phần mềm có một độ phức tạp vốn có, mà tôi gọi độ phức tạp bản chất. Độ phức tạp phát sinh từ các thỏa hiệp mà chúng ta thực hiện làm mắc nợ kỹ thuật là chuyện khác. Nó bao gồm tất cả các cách đã áp đặt từ bên ngoài làm cho phần mềm trở nên phức tạp và nó sẽ không tồn tại trong một thế giới hoàn hảo. Tôi gọi đó là độ phức tạp ngẫu nhiên. Tôi định nghĩa và thảo luận về các thuật ngữ này tỷ mỉ và cẩn thận trong cuốn sách Nhà lập trình năng suất cao (The Productive Programmer) của tôi (xem Tài nguyên). Các thuật ngữ này nói chung không phải là không có gì mới mẻ: chúng tồn tại trên một dải phổ, giống như thiết kế. Một vài ví dụ sẽ giúp làm rõ sự khác biệt này.

Một trong những đồng nghiệp của tôi làm việc với một hệ thống bảng lương cho một công ty có tổ chức công đoàn. Một trong những sự nhượng bộ mà công đoàn đã đạt được dành cho một số trong các thành viên của họ là được thêm một ngày nghỉ làm nữa vào đầu mùa săn bắn. (Này, họ chắc phải có những người đàm phán tốt đấy chứ). Những công nhân đang nói đến ấy làm việc tại chỉ một nhà máy, nhưng phụ cấp thêm cho một ngày nghỉ nữa là một phần hợp pháp của hệ thống bảng lương cho toàn công ty. Việc thay đổi này làm tăng thêm khá nhiều tính phức tạp của phần mềm, nhưng nó là độ phức tạp bản chất vì nó đã là một phần vấn đề nghiệp vụ cần giải quyết.

Một ví dụ khác, đi xa hơn một chút trong phạm vi thiết kế, luôn luôn xuất hiện: bảo mật ở mức các trường của các biểu mẫu nhập dữ liệu. Rất nhiều doanh nhân nghĩ họ muốn có quyền kiểm soát các đặc tính bảo mật của mỗi trường với độ chi tiết cao. Trong thực tế, họ hầu như luôn luôn ghét nó khi nó được thực thi bởi vì nó tạo ra một gánh nặng cho những người sử dụng, những người cần phải định nghĩa và duy trì tất cả các siêu dữ liệu này. Các doanh nhân ở một trong các dự án của chúng tôi thực sự muốn có tính năng này, do đó chúng tôi thực thi một phần của nó trên một trong các màn hình dành cho họ. Một khi họ lần đầu tiên tận mắt nhìn thấy cần bao nhiêu nỗ lực để làm cho nó hoạt động, họ đã quyết định rằng, do chỉ có quyền truy cập vào ứng dụng này từ một văn phòng có khóa, họ có thể đồng ý với bảo mật có mức chi tiết thô hơn. Đây là một ví dụ hay về một quyết định thiết kế xuất hiện ngay khi doanh nhân nhìn thấy thực tế những gì mà họ nghĩ rằng họ muốn có.

Tại đầu mút bên kia của dải phổ theo hướng độ phức tạp ngẫu nhiên là các bài tập đo độ sâu thuần túy giống như hai phiên bản đầu tiên của công nghệ Enterprise JavaBeans (EJB) và các công cụ như BizTalk. Một vài dự án cần làm thêm công việc mà các công cụ này đưa vào, nhưng chúng chẳng làm được bất kỳ điều gì ngoài việc tăng thêm độ phức tạp cho hầu hết các dự án có sử dụng chúng.

Ba điều có khuynh hướng sinh ra độ phức tạp ngẫu nhiên. Tôi đã thảo luận điều thứ nhất: dùng tạm một giải pháp vừa kịp có để viết mã vì lý do lịch trình công việc hoặc vì các áp lực bên ngoài khác. Điều thứ hai là sao chép, điều mà cuốn sách Những nhà lập trình thực dụng gọi là sự vi phạm nguyên tắc không lặp lại chính mình (DRY - Don't Repeat Yourself). Sao chép là cách xảo quệt nhất làm giảm công sức trong việc phát triển phần mềm bởi vì nó có thể luồn vào nhiều chỗ đến mức thậm chí các nhà phát triển không thể nhận biết. Các ví dụ hiển nhiên là các mã lệnh cắt và dán, nhưng cũng có rất nhiều các ví dụ tinh vi hơn. Chẳng hạn, hầu như mọi dự án có sử dụng một công cụ lập sơ đồ đối tượng-quan hệ (ví dụ như Hibernate hoặc iBatis) có rất nhiều chỗ sao chép lại. Lược đồ cơ sở dữ liệu của bạn, các tệp tin ánh xạ XML và các POJO hậu thuẫn, chứa các thông tin hơi khác nhau một chút, nhưng chồng chéo nhau. Có thể sửa lỗi này bằng cách tạo ra một nguồn chuẩn tắc các thông tin này và sau đó sinh ra các phần khác. Bản sao gây hại cho các dự án vì nó chống lại các cố gắng để làm thay đổi cấu trúc hoặc tái cấu trúc lại theo hướng tạo mã tốt hơn. Nếu bạn biết rằng bạn cần phải thay đổi một thứ gì đó ở ba chỗ khác nhau, bạn hãy tránh làm điều này ngay cả khi nó sẽ làm cho mã tốt hơn trong dài hạn.

Điều thứ ba có thể làm phát sinh độ phức tạp ngẫu nhiên là tính không đảo ngược được. Bất kỳ quyết định nào mà bạn thực hiện không thể đảo ngược, cuối cùng sẽ dẫn đến một độ phức tạp ngẫu nhiên nào đó. Tính không đảo ngược được tác động đến cả hai việc kiến trúc lẫn thiết kế, mặc dù các ảnh hưởng của nó là vừa phổ biến hơn và vừa gây tổn hại nhiều hơn ở mức độ kiến trúc. Hãy cố gắng tránh các quyết định không có khả năng hay rất khó đảo ngược. Một câu thần chú Ấn độ mà tôi đã nghe một số đồng nghiệp của tôi sử dụng là chờ cho tới thời điểm hợp lý cuối cùng. Điều này không có nghĩa là bạn để lại các quyết định quá kéo dài mà chỉ vừa đủ dài. Thời điểm hợp lý cuối cùng mà bạn có thể đưa ra một quyết định về một mối quan tâm kiến trúc nào đó là gì? Bạn còn có thể tránh ra quyết định càng lâu, thì các khả năng còn mở cho chính bạn càng nhiều. Hãy tự hỏi: "Tôi có cần phải đưa ra quyết định ngay bây giờ không?" và "Tôi có thể làm gì để cho phép tôi trì hoãn quyết định đó?" Bạn sẽ ngạc nhiên về những điều mà bạn có thể trì hoãn cho đến sau này nếu bạn chỉ cần áp dụng một số tài khéo léo cho quá trình ra quyết định của bạn.

Sự khác biệt mà tôi đã nêu ở trên giữa kiến trúc mức khung công tác và kiến trúc ứng dụng buộc chặt vào nguyên tắc về thời điểm hợp lý cuối cùng. Kiến trúc ứng dụng có khuynh hướng trở thành một kiến trúc logic. Ví dụ, giả sử bạn biết rằng bạn muốn phân tách các mối quan tâm về Mô hình-Khung nhìn-Trình bày. Rất thông thường, bạn thực hiện bước nhảy ngay đến việc thực thi vật lý của kiến trúc logic đó bằng cách chọn một khung công tác đáp ứng được một số hoặc tất cả các yêu cầu. Hãy xem xem bạn có thể trì hoãn quyết định đó không vì một khi bạn đã có thực thi vật lý triển khai rồi, nó khống chế các loại quyết định khác mà bạn phải xem xét kỹ. Việc tạm để ra ngoài chưa xét một quyết định về khung công tác cho đến khi còn có thể, sẽ để lại cho bạn những khả năng mở đối với các tùy chọn tốt hơn, ít bị ô nhiễm bởi hoàn cảnh thực tế.

Tính bao quát tràn lan

Điều cuối cùng trong số các mối quan tâm bao trùm dành cho kiến trúc và thiết kế là một cụm từ tôi đã tạo ra được gọi là tính bao quát tràn lan (rampant genericness). Chúng ta hình như có một căn bệnh trong thế giới Java: bày ra quá nhiều các giải pháp bằng cách cố gắng làm cho chúng càng bao quát chung càng tốt. Động lực cho điều này là rõ ràng: Nếu chúng ta xây dựng sẵn nhiều tầng dành cho việc mở rộng, chúng ta có thể xây dựng thêm bên trên chúng một cách dễ dàng hơn về sau này. Tuy nhiên, đây là một bẫy nguy hiểm. Bởi vì tính bao quát làm tăng thêm entropi, gây tổn hại đến khả năng làm tiến triển thiết kế theo những cách đáng quan tâm ngay từ đầu trong dự án. Thêm quá nhiều tính linh hoạt làm cho mọi thay đổi đối với cơ sở mã nguồn thành phức tạp hơn.

Tất nhiên, bạn không thể bỏ qua khả năng mở rộng. Phong trào ủng hộ thiết kế nhanh nhẹn (agile) có một cụm từ thú vị tóm tắt quá trình quyết định việc thực thi các đặc tính: YAGNI (Bạn sẽ không cần nó). Đây là một câu thần chú Ấn độ để cố gắng tránh bày ra quá nhiều cho một đặc tính đơn giản. Chỉ cần thực thi chính xác những gì bạn cần bây giờ và nếu sau này bạn cần thêm nhiều thứ khác thì bạn có thể thêm nó sau. Tôi đã thấy có một số dự án Java phình to ra với các thỏa hiệp trong cả kiến trúc lẫn thiết kế để hiến tế cho tính bao quát và khả năng mở rộng đến nỗi làm cho dự án thất bại. Tất nhiên điều này thật là mỉa mai vì chính việc lên kế hoạch cho dự án trường tồn lại rút ngắn cuộc sống của nó. Việc tìm hiểu cách làm thế nào để chèo lái qua khe nhỏ giữa khả năng mở rộng và bày ra quá nhiều là rất khó và nó là một chủ đề mà tôi sẽ trở lại thường xuyên


Bản đồ chỉ đường

Bài viết này chứa rất nhiều cái phẩy tay (hàm ý tạm cho qua) và không có mã nguồn làm cho nó không giống như tất cả các bài viết khác sắp tới trong loạt bài này. Một trong những vấn đề vốn có trong việc thảo luận các chủ đề phức tạp như kiến trúc và thiết kế là việc thiết lập bối cảnh phải xuất hiện để chắc chắn rằng tất cả mọi người ở trên cùng một trang. Tôi đã thiết lập khung cảnh cho phần còn lại của loạt bài này, ở đó tôi sẽ xoáy sâu vào các lĩnh vực cụ thể liên quan đến kiến trúc tiến hóa và thiết kế nổi dần. Mỗi bài viết sẽ đi sâu vào một khía cạnh minh họa cụ thể của một hoặc cả hai khái niệm này với khá nhiều chi tiết và mã nguồn. Phần tiếp theo: Tôi nói về thiết kế nổi dần thông qua việc phát triển dựa theo thử nghiệm, mà tôi đã đổi tên thành thiết kế dựa theo thử nghiệm.

Tài nguyên

Học tập

  • "Who Needs an Architect?" (Martin Fowler, IEEE Software, 09.2003): Hãy đọc sách trắng kinh điển của Fowler.
  • The Productive Programmer (Neal Ford, O'Reilly Media, 2008)): Cuốn sách gần đây nhất của Neal Ford mở rộng một số chủ đề trong bài viết này.
  • The Pragmatic Programmer (Andy Hunt and Dave Thomas, The Pragmatic Bookshelf, 2001): Cuốn sách này bao gồm một cuộc thảo luận về tác động của entropi đối với phần mềm.
  • Essential XP: Emergent Design (Ron Jeffries): Cuộc thảo luận trên Web với những suy xét về thiết kế nổi dần trong thế giới lập trình đỉnh cao.
  • "Emergent Optimization in Test Driven Design" (Michael Feathers): Thử nghiệm như thế nào để giúp tránh được sự tối ưu hóa quá sớm.
  • Duyệt qua technology bookstore để tìm các sách về chủ đề kỹ thuật này và các chủ đề kỹ thuật khác.
  • developerWorks Java technology zone: Tìm hàng trăm bài viết về mọi khía cạnh của lập trình Java.

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Công nghệ Java
ArticleID=390983
ArticleTitle=Kiến trúc tiến hóa và thiết kế nổi dần: Nghiên cứu điều tra kiến trúc và thiết kế
publish-date=05202009