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
Neal Ford, Kiến trúc phần mềm, ThoughtWorks
Tóm tắt: 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.
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óa và thiế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ụng và kiế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ầ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óa và nổ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
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.
Mục lục
Định nghĩa kiến trúc
Định nghĩa thiết kế
Các mối quan tâm về kiến trúc và thiết kế
Bản đồ chỉ đường