SlideShare a Scribd company logo
1 of 28
Download to read offline
Patterns of Enterprise Application
Architecture
By Martin Fowler, David Rice, Matthew Foemmel, Edward Hieatt, Robert Mee,
Randy Stafford
Table of Contents
1 Giới thiệu chung..................................................................................................................3
2 Chapter 1. Layering...........................................................................................................14
3 Chapter 2. Organizing Domain Logic....................................................................................22
1 Giới thiệu chung
Architecture
'Kiến trúc' (Architecture) là một thuật ngữ mà rất nhiều người cố gắng để định nghĩa. Có 2
yếu tố chung (common elements):
 Một là sự phân chia (breakdown) của một hệ thống thành các thành phần (parts) của
hệ thống đó ở mức cao nhất (highest-level).
 Yếu tố khác là, các quyết định là rất khó thay đổi (decisions that are hard to
change).
Không chỉ có một cách để phát biểu (state) kiến trúc của một hệ thống; đúng hơn, có nhiều
kiến trúc trong một hệ thống, và quan điểm/cách nhìn nhận xem tầm quan trọng kiến trúc là
cái gì (the view of what is architecturally significant) là một yếu tố có thể thay đổi trong suốt
lifetime của một hệ thống.
Trong một bài viết, Ralph Johnson đưa ra quan điểm rằng architecture là một cái gì đó chủ
quan (subjective thing), một sự chia sẻ hiểu biết (shared understanding) về thiết kế của một
hệ thống bởi các chuyên gia phát triển trong một dự án. Thông thường, việc chia sẻ hiểu biết
này theo dạng các thành phần chính của hệ thống và cách chúng tương tác với nhau (major
components of the system and how they interact). Cũng về các quyết định (decisions),
các developer mong muốn các quyết định của họ có thể đúng ngay từ sớm vì họ nhận thức
rằng nó khó để thay đổi.
Cuốn sách này trình bày về các thành phần chính (major parts) của ứng dụng enterprise và
các quyết định (decisions) có thể nhận được ngay từ sớm. Tập trung vào kiến trúc phân tầng
(layer). Và trình bày về cách chia một hệ thống enterprise vào các layers và cách các layer
làm việc với nhau (how you decompose an enterprise application into layers and how
these layers work together). Hầu hết các ứng dụng nontrivial enterprise sử dụng kiến
trúc layers theo một số dạng, nhưng trong một số tình huống tiếp cận khác, chẳng hạn như
pipes hay filters, có giá trị (valuable).
Ứng dụng Enterprise
Rất nhiều người viết phần mềm máy tính (computer software), và chúng ta gọi tất cả là
phát triển phần mềm (software development). Tuy nhiên, có nhiều loại phần mềm khác
nhau, mỗi loại đều có thách thức và sự phức tạp riêng. Điều này xuất phát ra khi tôi nói
chuyện với một số bạn bè của tôi trong lĩnh vực viễn thông. Trong một số cách, ứng dụng
enterprise là dễ hơn nhiều so với các phần mềm viễn thông-chúng ta không có vấn đề xử lý
đa luồng rất khó khăn, và chúng ta không phải tích hợp phần cứng và phần mềm. Nhưng
theo những cách khác nó khó hơn nhiều. Các ứng dụng enterprise thường có dữ liệu phức
tạp (complex data) – và rất nhiều, cùng với các business rule mà 'fail all tests of
logical reasoning. Mặc dù một số techiniques và patterns phù hợp cho tất cả các loại phần
mềm, nhưng nhiều cái chỉ thích hợp cho duy nhất một ngành cụ thể (particular branch).
Các patterns trong cuốn sách này tập trung để giải quyết các vấn đề cho các ứng dụng
enterprise. Các thuật ngữ khác cho các ứng dụng enterprise bao gồm "các hệ thống thông
tin (information systems)" hoặc, "xử lý dữ liệu (data processing)".
Ta sẽ bắt đầu với các ví dụ. Ứng dụng Enterprise bao gồm payroll, patient records, shipping
tracking, cost analysis, credit scoring, insurance, supply chain, accounting, customer sevice,
và kinh doanh ngoại hối. Ứng dụng Enterprise không bao gồm tiêm automobile fuel
injection, xử lý văn bản, điều khiển thang máy, điều khiển nhà máy hóa chất, thiết bị
chuyển mạch điện thoại, hệ điều hành, các trình biên dịch, và các trò chơi.
Ứng dụng Enterprise thường liên quan đến persistent data. Các dữ liệu là persistent bởi vì
nó cần là di chuyển vòng quanh (around) giữa nhiều chương trình chạy – trên thực tế, nó
thường cần persist trong nhiều năm. Cũng trong thời gian này sẽ có rất nhiều thay đổi trong
các chương trình sử dụng nó. Nó thường sẽ tồn tại lâu hơn (outlast) các phần cứng
(hardware), các hệ điều hành (OS) và các trình biên dịch (compilers). Trong thời gian đó
sẽ có nhiều thay đổi về cấu trúc dữ liệu (the structure of the data) để lưu trữ các phần
mới (new pieces) của thông tin mà không làm phiền (disturbing) các phần cũ (old
pieces). Thậm chí nếu có một thay đổi cơ bản và các công ty cài đặt một ứng dụng hoàn
toàn mới để xử lý một công việc, các dữ liệu phải được di chuyển (migrated) đến các ứng
dụng mới.
Thường có rất nhiều dữ liệu (a lot of data) -- một hệ thống vừa phải sẽ có hơn 1 GB dữ liệu
được tổ chức trong hàng chục triệu bản ghi (records) – quản lý data là một phần quan
trọng (major part) của hệ thống. Các hệ thống cũ sử dụng các cấu trúc tập tin chỉ mục
(indexed file structures) như IBM VSAM và ISAM. Các hệ thống hiện đại thường sử dụng
cơ sở dữ liệu (database), phần lớn cơ sở dữ liệu quan hệ (relational database). Việc thiết
kế và feeding các cơ sở dữ liệu đã biến thành một subprofession của riêng nó.
Thông thường có nhiều người đồng thời (concurrently) truy cập vào dữ liệu. Đối với nhiều
hệ thống số người truy cập có thể ít hơn một 100 người, nhưng đối với các hệ thống Web-
based qua Internet số người truy cập tăng lên theo bậc (orders of magnitude). Đối với quá
nhiều người, có những vấn đề nhất định (definite issues) trong việc đảm bảo rằng tất cả
người dùng có thể truy cập vào hệ thống đúng cách. Nhưng ngay cả khi không có quá nhiều
người, vẫn còn có những vấn đề trong việc đảm bảo rằng hai người không truy cập vào cùng
một dữ liệu tại cùng thời điểm trong một cách mà gây ra lỗi. Các công cụ quản lý transaction
xử lý một số gánh nặng (burden) này, nhưng thường nó không thể che giấu (hide) điều này
từ các nhà phát triển ứng dụng.
Với rất nhiều dữ liệu, thường có nhiều UI screens để xử lí nó. Nó không phải bất thường khi
có tới hàng trăm screen riêng biệt. Người sử dụng các ứng dụng enterprise khác nhau (vary)
từ thỉnh thoảng occasional tới thường xuyên (regular), và thông thường họ sẽ có ít chuyên
môn kỹ thuật. Do đó, các dữ liệu đã được trình bày nhiều cách khác nhau cho các mục đích
khác nhau. Các hệ thống thường có rất nhiều (batch processing), cái dễ để quên khi tập
trung vào use case mà bắt buộc (stress) sự tương tác của người dùng.
Các ứng dụng Enterprises hiếm khi sống trên một 'hòn đảo' (island). Thông thường, chúng
cần phải tích hợp với các ứng dụng enterprise khác nằm rải rác xung quanh enterprise.
Các hệ thống khác nhau được xây dựng vào những thời điểm khác nhau, có technologies
khác nhau, và thậm chí cả các cơ chế hợp tác (collaboration mechanisms) sẽ khác nhau:
các file dữ liệu COBOL, CORBA, messaging systems. Vì vậy thông thường các enterprise sẽ
cố gắng để tích hợp với các hệ thống khác nhau của nó bằng cách sử dụng một công nghệ
communication chung (common communication technology). Tất nhiên, hầu như không bao
giờ kết thúc job, vì thế, có một vài unified integration schemes khác nhau cùng một lúc.
Điều này thậm chí tồi tệ hơn khi các enterprise tìm cách tích hợp với các đối tác kinh doanh
của họ.
Ngay cả khi một công ty hợp nhất các công nghệ cho integration, họ gặp vấn đề với sự khác
biệt trong quá trình kinh doanh (business process) và sự bất hòa khái niệm (conceptual
dissonance) với các dữ liệu. Một bộ phận của công ty có thể nghĩ rằng một customer là một
người có một current agreement; bộ phận khác cũng đếm những người đã có một hợp đồng
nhưng không làm được nữa (don't any longer); bộ phận khác đếm product sales nhưng
không service sales. Điều đó nghe có vẻ dễ dàng, nhưng khi bạn có hàng trăm records,
trong đó mỗi field có thể có một ý nghĩa khác nhau, kích thước tuyệt đối (sheer size) của
vấn đề sẽ trở thành một thách thức -- ngay cả nếu người duy nhất biết field này thực sự có
nghĩa là gì với công ty. (Và, tất nhiên, tất cả điều này thay đổi mà không báo trước). Kết
quả là, dữ liệu phải được liên tục đọc, munged, và viết bằng tất cả các loại định dạng cú
pháp và ngữ nghĩa khác nhau.
Sau đó có các vấn đề của những cái đi kèm theo thuật ngữ "business logic". Khi bạn xây
dựng một hệ điều hành bạn cố gắng để giữ cho tất cả mọi thứ (whole thing) hợp lý. Nhưng
các business rules được đưa cho bạn, và không có major political effort không có gì bạn có
thể làm gì để thay đổi chúng. Bạn phải đối phó với một mảng lộn xộn (haphazard array) của
các điều kiện kỳ lạ mà chúng thường tương tác với nhau theo các cách đáng ngạc nhiên
(surprising ways). Tất nhiên, họ nhận được lộ trình đó (that way) đó vì một lý do: Một số
nhân viên bán hàng đàm phán để có một khoản thanh toán hàng năm nhất định muộn hơn
bình thường do đó phù hợp với chu kỳ kế toán khách hàng của mình và vì thế giành được
một vài triệu USD trong kinh doanh. Một vài ngàn các trường hợp đặc biệt một lần là những
gì dẫn đến việc kinh doanh phức tạp "illogic" mà làm cho phần mềm kinh doanh rất khó
khăn. Trong tình huống này, bạn phải tổ chức business logic hiệu quả như bạn có
thể, bởi vì điều chắc chắn duy nhất là logic sẽ thay đổi theo thời gian.
Đối với một số người, thuật ngữ "enterprise application" ngụ ý một hệ thống lớn. Tuy nhiên,
điều quan trọng là phải nhớ rằng không phải tất cả các ứng dụng enterprise là rất lớn, mặc
dù chúng có thể cung cấp rất nhiều giá trị cho enterprise. Nhiều người cho rằng, khi hệ
thống nhỏ không lớn, chúng không đáng bận tâm. Nếu một hệ thống nhỏ thất bại, nó
thường làm make noise ít hơn so với một hệ thống lớn. Nếu bạn có thể làm gì đó cải thiện
các dự án nhỏ, thì là hiệu ứng tích lũy (cumulative effect) có thể rất đáng kể về một
enterprise, đặc biệt là kể từ khi dự án nhỏ thường có giá trị không tương xứng. Thật vậy,
một trong những điều tốt nhất bạn có thể làm là quay một dự án lớn thành một nhỏ (turn a
large project into a small one) bởi đơn giản hóa kiến trúc và quá trình của nó (simplifying
its architecture and process) .
Các loại ứng dụng Enterprise
Khi chúng ta thảo luận làm thế nào để thiết kế các ứng dụng enterprise (How to design
enterprise application), và các patterns nào được sử dụng (What patterns to use),
chúng ta nhận ra một điều rất quan trọng là các ứng dụng enterprise đều khác nhau và
các vấn đề khác nhau đó dẫn đến nhiều cách khác nhau để đối ứng. Có rất nhiều các
phương án khác nhau để lựa chọn, nhưng ở đây tôi sẽ chọn 3 points trên big plane rất lớn
này.
Hãy xem xét một B2C (business to customer) bán lẻ trực tuyến (online retailer): Người
sử dụng duyệt web -- tạo giỏ mua hàng – mua hàng. Đối với một hệ thống như vậy, chúng
ta cần có khả năng xử lý một số lượng rất lớn người sử dụng (very high volumn of users),
vì vậy giải pháp của chúng ta cần đưa ra không chỉ hợp lý về hiệu quả sử dụng tài nguyên
(reasonably efficient in terms of resources) mà còn có thể mở rộng (scalable) để bạn
có thể làm tăng load (increase the load) bằng cách thêm phần cứng nhiều hơn (adding
more hardware). Domain logic cho một ứng dụng như vậy có thể khá đơn giản: order
capturing, some relatively simple pricing, shipping calculations, và shipment notification.
Chúng ta muốn bất cứ ai đều có thể truy cập vào hệ thống một cách dễ dàng, do đó kéo
theo (implies) một pretty generic Web presentation có thể được sử dụng với phạm vi
rộng nhất có thể của các browsers. Data source bao gồm một cơ sở dữ liệu (database) để
tổ chức (holding) các order và có lẽ một số communication với một hệ thống inventory
để giúp cho information luôn available và delivery.
Ngược lại với hệ thống trên là một hệ thống tự động hóa xử lý các thỏa thuận cho thuê (A
system automates the processing of leasing agreements). Trong một vài cách thì hệ
thống này đơn giản hơn nhiều so với B2C retailer vì có ít người dùng hơn nhiều, không
quá 100 users tại một thời điểm. Nó phức tạp hơn là trong business logic. Tính toán các
hóa đơn hàng tháng (Calculating monthly bills) trên một hợp đồng thuê, xử lý các event như
early returns và late payments, và validating data như một lease được book là nhiệm vụ
phức tạp, từ khi phần lớn các leasing industry's competition (đối thủ cạnh tranh) đi vào
(come in) các dạng ít biến đổi hơn các giao dịch (deals) được thực hiện trong quá khứ . Một
complex business domain như thế này là một thách thức vì các quy định là rất arbitrary (độc
đoán).
Một hệ thống như vậy cũng phức tạp hơn trong giao diện người dùng (UI). Ít nhất, điều
này có nghĩa một giao diện HTML được gọi nhiều hơn với các screens phức tạp hơn (this
means a much more involved HTML interface with more, and more complex, screens).
Thường thì các hệ thống này có các UI demands (nhu cầu), cái mà dẫn các users tới mong
muốn có một presentation phức tạp hơn so với một HTML front end cho phép, do đó, một
giao diện rich-client thông thường (conventional) hơn là cần thiết. Một tương tác người dùng
phức tạp hơn (more complex user interaction) cũng dẫn đến hành vi giao dịch (transaction
behavior) phức tạp hơn: Booking a lease (đặt một hợp đồng thuê) có thể mất một hay hai
giờ, trong thời gian đó người sử dụng trong một giao dịch hợp lý (logical transaction). Chúng
ta cũng thấy một database schema phức tạp với khoảng hai trăm tables và các
connections với các packages cho việc đánh giá tài sản (asset valuation) và định giá
(pricing).
Một điểm ví dụ thứ ba là một expense-tracking system cho một công ty nhỏ. Một hệ
thống như vậy có vài users và logic đơn giản và có thể dễ dàng được truy cập trên toàn
công ty với một HTML presentation. Data source chỉ một vài tables trong một database.
Đơn giản như nó là, một hệ thống như thế này không phải là không có những thách thức.
Bạn phải xây dựng nó rất nhanh chóng và bạn phải nhớ (bear in mind) rằng nó có thể
phát triển như người ta muốn tính toán kiểm tra hoàn thuế (reimbursement checks), feed
chúng vào hệ thống tính lương (payroll system), hiểu biết về thuế (understand tax
implications), cung cấp các báo cáo cho các giám đốc tài chính (CFO), buộc (tie) vào các
airline reservation Web services... Cố gắng sử dụng các architecture cho một trong hai
system thí dụ khác sẽ làm chậm sự phát triển của hệ thống này. Nếu một hệ thống có lợi ích
kinh doanh (như tất cả các ứng dụng enterprise nên), trì hoãn những lợi ích chi phí tiền bạc
(delaying those benefits costs money). Tuy nhiên, bạn không muốn thực hiện các quyết định
bây giờ mà sẽ cản trở sự tăng trưởng trong tương lai. Nhưng nếu bạn bổ sung thêm sự
linh hoạt (flexibility) ngay từ bây giờ và mắc phải sai lầm (get it wrong), sự phức tạp
được thêm vì tính linh hoạt của thực sự có thể làm cho nó khó khăn hơn để phát triển
trong tương lai và có thể trì hoãn việc triển khai và do đó trì hoãn lợi ích. Mặc dù hệ
thống như vậy có thể là nhỏ, hầu hết các enterprise có rất nhiều hệ thống như vậy vì thế
hiệu quả tích lũy của một kiến trúc không phù hợp có thể là đáng kể (most enterprises have
a lot of them so the cumulative effect of an inappropriate architecture can be significant.)
Mỗi ứng dụng enterprise trong 3 thí dụ có các khó khăn, và chúng có các khó khăn khác
nhau. Kết quả là bạn không thể chỉ với một architecture duy nhất mà sẽ giải quyết được cho
cả ba. Chọn một architecture có nghĩa là bạn phải hiểu được những vấn đề cụ thể
của hệ thống của bạn và chọn một thiết kế thích hợp dựa trên sự hiểu biết đó. Đó là
lý do tại sao trong cuốn sách này, tôi không đưa ra một giải pháp duy nhất cho nhu cầu của
doanh nghiệp của bạn. Thay vào đó, có nhiều pattern cho sự lựa chọn và thay thế.
Ngay cả khi bạn chọn một pattern cụ thể, bạn sẽ phải thay đổi nó để đáp ứng nhu
cầu của bạn. Bạn không thể xây dựng phần mềm enterprise mà không cần suy nghĩ, và tất
cả bất kỳ cuốn sách có thể làm là cung cấp cho bạn thêm thông tin cho cơ sở quyết định của
bạn trên.
Nếu điều này áp dụng cho patterns, nó cũng áp dụng cho các tools. Mặc dù nó rõ
ràng là cần sense (cảnh giác) để lựa chọn (pick) một tập các tool để phát triển các ứng
dụng, bạn cũng phải nhận ra (recognize) các tools khác nhau là tốt nhất cho các mục đích
khác nhau. Hãy coi chừng của việc sử dụng một tool thực sự là phù hợp với một loại ứng
dụng khác nhau -- nó có thể cản trở nhiều hơn là giúp đỡ (it may hinder more than help).
Suy nghĩ về hiệu năng – Performance
Nhiều quyết định architecture là về hiệu suất (performance). Đối với hầu hết các vấn đề
performance tôi thích có một system, đang chạy (running), đo đạc (instrument) nó, và sau
đó sử dụng một quá trình tối ưu disciplined (có kỉ luật) dựa trên phép đo. Tuy nhiên, một số
quyết định architecture ảnh hưởng đến hiệu suất theo một cách mà khó để fix tối
ưu hóa sau này. Và ngay cả khi nó rất dễ để fix, người tham gia dự án phải lo lắng về
những quyết định sớm.
Luôn luôn khó khăn để nói về performance trong một cuốn sách như thế này. Lý do là nó
quá khó để đưa ra bất cứ lời khuyên nào về performance, nó không nên được coi là thực tế
(fact) cho đến khi nó được đo đạc trên cấu hình (configuration) của bạn. Tôi đã từng nhìn
thấy các design được sử dụng hoặc bị reject vì cân nhắc hiệu suất, mà hóa ra không có một
ai thực sự làm một số phép đo trên các setup thực tế được sử dụng cho các ứng dụng một
lần.
Tôi đưa ra một số guidelines trong cuốn sách này, bao gồm cả việc tổi thiểu các remote
calls. Mặc dù vậy, bạn nên kiểm tra mỗi lời khuyên (tip) bằng cách đo trên ứng dụng của
bạn. Tương tự như vậy có several occasions (một vài công việc) nơi mà code ví dụ trong
cuốn sách này hy sinh (sacrifice) performance cho dễ hiểu (understandability). Một lần tùy
thuộc vào quyết định của bạn (it's up to you) để áp dụng tối ưu hóa cho môi trường
của bạn. Bất cứ khi nào bạn thực hiện tối ưu hóa hiệu suất, tuy nhiên, bạn phải đo
cả trước và sau, nếu không, bạn chỉ có thể làm cho code của bạn khó đọc hơn.
Có một hệ quả (corollary) quan trọng này: Một sự thay đổi đáng kể trong configuration
có thể vô hiệu bất kỳ sự thật (invalidate any facts) về hiệu suất. Vì vậy, nếu bạn
nâng cấp lên một phiên bản mới của virtual machine, hardware, database, hoặc bất cứ điều
gì khác, bạn phải redo tối ưu hóa hiệu suất của bạn và đảm bảo rằng chúng vẫn đang
được giúp đỡ. Trong nhiều trường hợp một configuration mới có thể thay đổi mọi
thứ. Thật vậy, bạn có thể thấy rằng một tối ưu hóa mà bạn đã làm trong quá khứ để cải
thiện performance thực sự làm hư (hurts) performance trong môi trường mới. Một vấn đề
khác khi nói về hiệu suất là thực tế có nhiều thuật ngữ được sử dụng một cách inconsistent
(không tương xứng/không nhất quán). Nạn nhân (victim) được chú ý nhiều nhất của việc
này là thuật ngữ "khả năng mở rộng (scalability)", nó thường xuyên được sử dụng có nghĩa
như half of dozen (nửa tá) những cái khác nhau. Dưới đây là các thuật ngữ tôi sử dụng.
Thời gian đáp ứng (response time) là lượng thời gian (amount of time) cần cho hệ thống
(it takes for the system) xử lý một request từ bên ngoài. Đây có thể là UI action, chẳng hạn
như nhấn một nút, hoặc call một Server API.
Đáp ứng (responsiveness) là về một hệ thống công nhận (acknowledges) một request
nhanh như thế nào (how quickly the system acknowledges a request) trái ngược với
việc xử lí nó (as opposed to processing it). Điều này rất quan trọng trong nhiều hệ thống
bởi vì người dùng có thể thất vọng nếu một hệ thống có responsiveness thấp, thậm chí
nếu response time của nó là tốt. Nếu hệ thống của bạn chờ đợi (waiting) trong suốt
toàn bộ yêu cầu (whole request), thì responsiveness và response time là như nhau.
Tuy nhiên, nếu bạn tỏ ra (indicate) rằng bạn đã nhận được request trước khi bạn
hoàn thành, thì responsiveness của bạn là tốt hơn. Cung cấp một progress bar trong
suốt quá trình copy file cải thiện responsiveness của UI của bạn, mặc dù nó không cải thiện
response time.
Độ trễ (latency) là thời gian tối thiểu cần thiết để có được bất kỳ form của response,
thậm chí nếu công việc phải được thực hiện là không tồn tại. Đó thường là những vấn đề lớn
trong các hệ thống từ xa (remote systems). Nếu tôi yêu cầu (ask) một chương trình không
làm gì cả, nhưng để cho tôi biết (to tell me) khi nó đã không không làm gì cả, thì tôi nên
nhận được response gần như tức thời (almost instantaneous ) nếu chương trình chạy trên
laptop của tôi. Tuy nhiên, nếu chương trình chạy trên một máy tính từ xa (remote
computer), tôi có thể mất một vài giây chỉ vì thời gian thực hiện đối với các request và
response để thực hiện theo cách của chúng trên đường truyền (wire). Là một application
developer, tôi thường không thể làm gì để cải thiện độ trễ (latency). Độ trễ cũng là lý do tại
sao bạn nên giảm thiểu các cuộc gọi từ xa (remote calls).
Thông lượng (throughput) là bao nhiêu stuff mà bạn có thể làm trong một khoảng thời gian
nhất định (amount of time). Nếu bạn đang tính giờ (timing) thời gian sao chép của một tập
tin, thông lượng có thể được đo bằng bytes/s. Đối với các ứng dụng enterprise là phép đo
thông thường (typical measure) là số transactions/s (tps). Nhưng vẫn đề là nó phục thuộc
vào độ phức tạp các transaction của bạn. Đối với hệ thống cụ thể của bạn, bạn nên chọn
một tập các transaction phổ biến.
Trong thuật ngữ performance này là một trong hai throughput hoặc response time – cái
nào quan trọng hơn đối với bạn. Đôi khi có thể khó khăn để nói về performance khi một
kỹ thuật cải thiện throughput nhưng giảm response time, vì vậy tốt nhất để sử dụng các
thuật ngữ chính xác hơn. Từ góc độ người dùng responsiveness có thể quan trọng hơn
response time, vì vậy nâng cao responsiveness phải trả bằng (at the cost of) response time
hoặc throughput sẽ làm tăng hiệu suất.
Tải (load) là một phát biểu (statement) có 'how much stress a system is under', mà có thể
đo được bằng có bao nhiêu users đang kết nối với nó (how many users are currently
connected to it). Load thường là một context cho một phép đo khác, chẳng hạn như
response time. Như vậy, bạn có thể nói rằng response time đối với một số request là 0,5s
với 10 users và 2s với 20 users.
Độ nhạy tải (load sensitivity) là một biểu hiện (expression) của response time thay đổi
như thế nào với load (how the response time varies with the load). Hãy nói rằng hệ thống A
có response time là 0,5s cho 10 users đến 20 users và hệ thống B có response time là 0,2s
cho 10 users và tăng lên đến 2s cho 20 users. Trong trường hợp này hệ thống A có load
sensitivity thấp hơn so với hệ thống B. Chúng tôi cũng có thể sử dụng sự suy thoái
(degradation) để nói rằng hệ thống B làm giảm hơn so với hệ thống A.
Hiệu quả (efficiency) là hiệu suất được phân chia bởi các resources. Một hệ thống mà được
30 TPS trên 2 CPUs có hiệu quả hơn một hệ thống mà được 40 TPS trên 4 CPUs giống hệt
nhau.
Khả năng của một hệ thống (capacity of a system) là một sử biểu thị (indication) của
effective throughput lớn nhất hoặc load. Đây có thể là một lớn nhất tuyệt đối (absolute
maximum) hay một điểm (point) mà tại đó performance lặn (dips) dưới một ngưỡng
(threshold) chấp nhận được.
Khả năng mở rộng (scalability) là một thước đo (measure) của thêm resource (thường
là hardware) ảnh hưởng đến performance như thế nào (how adding resouces affects
performance). Một hệ thống scalable cho phép bạn thêm hardware và đạt được cải thiện về
performance tương xứng, chẳng hạn như tăng gấp đôi số servers, bạn có tăng gấp đôi
throughput của bạn. Khả năng mở rộng theo chiều dọc (vertical scalability), hoặc
scaling up, có nghĩa là thêm nhiều power cho một server duy nhất, chẳng hạn như
nhiều memory hơn. Khả năng mở rộng theo chiều ngang (horizontal scalability), hay
scaling out, có nghĩa là thêm nhiều servers.
Vấn đề ở đây là các quyết định thiết kế (design decisions) không ảnh hưởng đến tất cả các
yếu tố performance như nhau (equally). Nói rằng chúng ta có hai hệ thống phần mềm chạy
trên một server: Capacity của Swordfish là 20 TPS trong khi Capacity của Camel là 40
TPS. Cái nào có performance tốt hơn? Cái nào có scalable hơn? Chúng ta không thể trả
lời câu hỏi scalable từ dữ liệu này, và chúng ta chỉ có thể nói rằng Camel là hiệu quả hơn
trên một server duy nhất. Nếu chúng ta thêm một server khác, chúng ta nhận thấy
rằng Swordfish bây giờ xử lý 35 TPS và Camel xử lý 50 TPS. Capacity của Camel là
vẫn tốt hơn, nhưng Swordfish có vẻ như nó có thể scale out tốt hơn. Nếu chúng ta
tiếp tục bổ sung thêm các servers chúng ta sẽ nhận ra rằng Swordfish được 15 TPS trên mỗi
server thêm và Camel được 10. Với những dữ liệu này, chúng ta có thể nói rằng Swordfish
scale out tốt hơn, mặc dù Camel là hiệu quả hơn khi có ít hơn 5 servers.
Khi xây dựng các hệ thống enterprise, nó thường make sense để xây dựng cho hardware
scalability hơn là capacity hoặc thậm chí efficiency. Scalability cung cấp cho bạn tùy chọn
performance tốt hơn nếu bạn cần nó. Scalability cũng có thể được dễ dàng hơn để làm.
Thường thì các designer làm những điều phức tạp để cải thiện capacity trên một nền tảng
phần cứng cụ thể (particular hardware platform) khi nó thực sự có thể rẻ hơn để nhiều
hardware hơn. Nếu Camel có một chi phí lớn hơn Swordfish, và chi phí lớn hơn tương đương
với một couple servers, thì Swordfish kết thúc (ends up) là rẻ hơn thậm chí nếu bạn chỉ cần
40 TPS. Nó là fashionable để phàn nàn về việc phải dựa vào hardware tốt hơn để làm cho
software của chúng ta chạy đúng, và tôi tham gia ca đoàn này bất cứ khi nào tôi phải nâng
cấp máy tính xách tay của tôi chỉ để xử lý các phiên bản mới nhất của Word. Nhưng
hardware mới hơn thường rẻ hơn so với làm cho phần mềm chạy trên các hệ thống
chưa mạnh mẽ. Tương tự như vậy, thêm nhiều servers thường rẻ hơn so với việc
thêm các programmers -- cung cấp một hệ thống scalable.
Các khuôn mẫu – Patterns
Không có định nghĩa một pattern chung nào được chấp nhận, nhưng có lẽ là nơi tốt nhất
(best place) để bắt đầu là Christopher Alexander, một nguồn cảm hứng cho nhiều người đam
mê pattern: "Mỗi pattern mô tả một vấn đề xảy ra lặp đi lặp lại (over and over again) trong
environment của chúng ta, và sau đó mô tả core của solution cho vấn đề đó, trong một cách
(in such a way) mà bạn có thể sử dụng solution này trên một triệu lần, mà không bao giờ
làm việc đó cùng một cách hai lần (doing it the same way twice)"[Alexander et al.].
Alexander là một kiến architect, vì vậy ông nói về các building, nhưng định nghĩa này làm
việc khá tốt đối với software. Trọng tâm của pattern (The focus of the pattern) là một giải
pháp cụ thể, một giải pháp common và hiệu quả trong việc giải quyết một hoặc nhiều
vấn đề tái diễn (recurring). Một cách nhìn khác là một pattern là một chunk of advice và
nghệ thuật tạo mẫu (the art of creating patterns) là divide up many pieces of advice
into relatively independent chunks để bạn có thể refer tới chúng và discuss chúng nhiều
hoặc ít hơn một cách riêng biệt.
Một key part của pattern là chúng bắt nguồn từ thực tế. Bạn tìm thấy các patterns bằng
cách nhìn vào những gì người ta làm (looking at what people do), quan sát những gì làm
việc (observing things that work), và sau đó tìm kiếm các "core of the solution“ Nó
không phải là một quá trình dễ dàng, nhưng một khi bạn đã tìm thấy một số pattern tốt
chúng trở thành một cái gì đó có giá trị (valuable thing). Đối với tôi giá trị của chúng nằm ở
chỗ có thể tạo ra một cuốn sách như là một tài liệu tham khảo. Bạn không cần phải đọc tất
cả các cuốn sách này, hoặc tất cả của bất kỳ cuốn sách pattern khác, để tìm thấy nó hữu
ích. Bạn chỉ cần phải đọc đủ (read enough) để ý thức (sense) các patterns là gì (what the
patterns are), chúng giải quyết các vấn đề gì (what problems they solve), và chúng giải
quyết vấn đề như thế nào (how they solve them). Bạn không cần phải biết tất cả các chi
tiết nhưng phải đủ để nếu bạn đi vào một trong những vấn đề thì bạn có thể tìm thấy các
pattern trong cuốn sách. Chỉ khi đó bạn cần hiểu thực sự sâu (in depth) về pattern.
Khi bạn cần pattern, bạn phải tìm hiểu làm thế nào để áp dụng (how to apply) nó vào hoàn
cảnh của bạn. Một điều quan trọng (key thing) về patterns là bạn không bao giờ áp dụng
các giải pháp một cách mù quáng, đó là lý do tại sao các pattern tools đã thất bại thê
thảm như vậy. Tôi muốn nói rằng các patterns là "half baked," điều đó có nghĩa là bạn luôn
luôn phải hoàn thành chúng trong lò nướng (oven) dự án của riêng bạn. Mỗi lần tôi sử dụng
một pattern tôi tinh chỉnh nó một chút ở chỗ này và một chút chỗ khác. Bạn thấy là 'same
solution many times over, but it's never exactly the same..'
Mỗi pattern là tương đối độc lập, nhưng các patterns không bị cô lập (isolated) với các
pattern khác. Thường thì một pattern dẫn đến một pattern khác hoặc một pattern xảy ra
(occurs) chỉ xung quanh pattern khác. Như vậy, bạn thường chỉ nhìn thấy Class Table
Inheritance nếu có một Domain Model trong thiết kế của bạn. Ranh giới giữa các pattern
là mờ (naturally fuzzy), nhưng tôi đã cố gắng để làm cho mỗi pattern như self-standing như
tôi có thể. Nếu ai đó nói "Sử dụng một Unit of Work," bạn có thể nhìn tìm nó và xem làm
thế nào để áp dụng nó mà không cần phải đọc toàn bộ cuốn sách.
Nếu bạn là một nhà thiết kế giàu kinh nghiệm cho các ứng dụng enterprise, bạn có thể sẽ
thấy rằng hầu hết các patterns đều quen thuộc với bạn. Tôi hy vọng bạn sẽ không quá thất
vọng. Các patterns không phải các ý tưởng gốc (original ideas); chúng tôi quan sát nhiều
những gì xảy ra trong lĩnh vực này. Kết quả là, chúng tôi không nói rằng chúng tôi "phát
minh (invented)" một pattern mà thay vào đó, chúng tôi đã "phát hiện (discovered)" ra một
pattern. Vai trò của chúng tôi là ghi chú (note) các giải pháp chung (common solution),
tìm kiếm cốt lõi của nó (its core), và sau đó viết ra các resulting pattern. Đối với một nhà
thiết kế giàu kinh nghiệm, giá trị của pattern không phải là nó cung cấp cho bạn một
ý tưởng mới; các giá trị nằm trong việc giúp bạn giao tiếp ý tưởng của bạn (the value lies
in helping you communicate your idea). Nếu bạn và đồng nghiệp của bạn đều biết
Remote Facade là gì, bạn có thể giao tiếp rất nhiều bằng cách nói, "Class này là một
Remote Facade." Nó cũng cho phép bạn nói với người mới, "Sử dụng một đối Data
Transfer Object cho điều này," và họ có thể đến với cuốn sách này để tìm nó. Kết quả là
các patterns đó tạo ra một vocabulary về thiết kế, đó là lý do tại sao đặt tên (naming) là
một vấn đề quan trọng như vậy.
Trong khi hầu hết các patterns thực sự truly cho ứng dụng enterprise, các patterns trong
chương base patterns là tổng quát hơn và localized (cục bộ). Tôi bao gồm họ, vì tôi tham
khảo chúng trong các cuộc thảo luận của các mô hình ứng dụng doanh nghiệp.
Cấu trúc của các khuôn mẫu – The Structure of the Patterns
Mỗi tác giả có lựa chọn pattern form của hoj. Một số form dựa trên một cuốn sách classic
patterns như [Alexander et al.], [Gang of Four], hoặc [POSA]. Những người khác làm theo ý
của họ. Tôi đã vật lộn với việc điều gì làm cho form là tốt nhất (what makes the best
form). Một mặt tôi không muốn một cái gì đó nhỏ như là GOF form; Mặt khác tôi cần phải
có section hỗ trợ một cuốn sách tham khảo. Vì vậy, đây là những gì tôi đã sử dụng cuốn
sách này.
Item đầu tiên là tên của Pattern. Tên Pattern là rất quan trọng, bởi vì một phần của mục
đích của các patterns là để tạo ra một vocabulary cho phép các designer giao tiếp hiệu quả
hơn. Vì vậy, nếu tôi nói cho bạn biết Web server của tôi được xây dựng xung quanh một
Front Controller và một Transform View và bạn biết các mô hình, bạn có một ý tưởng rất
rõ ràng về kiến trúc máy web server của tôi.
Tiếp theo là hai item đi với nhau: mục đích (intent) và bản phác thảo (sketch). Intent tổng
kết các patterns trong một hoặc hai câu; Sketch là một biểu diễn trực quan (visual
representation) của pattern, thường xuyên, nhưng không phải luôn luôn là một UML
diagram. Ý tưởng là để tạo ra một reminder ngắn gọn về what the pattern is about , do
đó bạn có thể nhanh chóng gọi nó. Nếu bạn thật sự "have the pattern," có nghĩa là bạn
biết các giải pháp ngay cả khi bạn không biết tên, thì intent và sketch nên là tất cả bạn cần
phải biết pattern là gì (what the pattern is).
Phần tiếp theo mô tả một motivating problem cho các pattern. Điều này có thể không phải là
vấn đề duy nhất mà các pattern giải quyết, nhưng nó là một cái mà tôi nghĩ là động lực tối
nhất cho pattern.
'How It Works' mô tả các giải pháp. Ở đây tôi đặt một cuộc discussion về vấn đề thực hiện
và variations (biến thể) tôi đã trải qua. Các discussion là độc lập nhất có thể với bất kỳ
platform cụ thể nào.
'When to Use It' mô tả khi nào các pattern nên được sử dụng. Ở đây tôi nói về trade-offs
cái mà làm cho bạn lựa chọn giải pháp này so với các giải pháp khác. Nhiều patterns trong
cuốn sách này là các lựa chọn thay thế: Page Controller và Front Controller. Vài pattern
luôn là lựa chọn đúng, vì vậy bất cứ khi nào tôi tìm thấy một pattern tôi luôn tự hỏi, "Khi
nào tôi không nên sử dụng điều này?" Câu hỏi đó thường dẫn tôi đến pattern thay thế.
Phần Further Reading chỉ bạn tới các thảo luận khác của pattern này. Đây không phải là
một tài liệu tham khảo toàn diện. Tôi đã giới hạn tài liệu tham khảo của tôi tới các pieces mà
tôi nghĩ là quan trọng trong việc giúp bạn hiểu các patterns, vì vậy tôi đã loại bỏ bất kỳ thảo
luận mà tôi không nghĩ bổ sung thêm nhiều cái với những gì tôi đã viết và tất nhiên tôi đã
loại bỏ các cuộc thảo luận của các pattern mà tôi chưa đọc. Tôi cũng đã không đề cập mục
mà tôi nghĩ rằng sẽ thật khó tìm được, hoặc liên kết Web không ổn định mà tôi sợ có thể
biến mất theo thời gian bạn đọc cuốn sách này.
Tôi muốn thêm một số ví dụ. Mỗi ví dụ là một ví dụ đơn giản của pattern sử dụng (in use),
minh họa bằng một số mã trong Java hoặc C #. Tôi đã chọn những ngôn ngữ đó bởi vì
chúng có vẻ là các ngôn ngữ mà số lượng lớn nhất của các lập trình viên chuyên nghiệp có
thể đọc. Các ví dụ này không phải là pattern. Khi bạn sử dụng các pattern, nó sẽ không
giống chính xác như ví dụ này nên không đối xử với nó như một loại glorified (vinh hiển / ca
ngợi) macro. Tôi đã cố tình giữ ví dụ như đơn giản càng tốt, do đó bạn có thể xem các
pattern trong một form rõ ràng như tôi có thể tưởng tượng. Tất cả các loại vấn đề bị bỏ qua
mà sẽ trở nên quan trọng khi bạn sử dụng nó, nhưng đó sẽ là vấn đề cụ thể với môi trường
của riêng bạn. Đây là lý do tại sao bạn luôn phải chỉnh pattern.
Một trong những hậu quả của việc này là tôi đã làm việc chăm chỉ để giữ cho mỗi ví dụ đơn
giản như tôi có thể, trong khi vẫn còn minh họa thông điệp cốt lõi của nó. Vì vậy, tôi thường
chọn một ví dụ đơn giản và rõ ràng, chứ không phải là một trong những minh chứng như thế
nào một mô hình hoạt động với nhiều wrinkles (nếp nhăn) yêu cầu trong một hệ thống sản
xuất. Đó là một sự cân bằng khéo léo (tricky balance) giữa sự đơn giản (simple) và đơn giản
(simplistic), nhưng thật sự có quá nhiều vấn đề peripheral (ngoại biên) thực tế có thể làm
cho khó khăn hơn để hiểu những điểm chính (key point) của một pattern.
Đây cũng là lý do tại sao tôi cho các ví dụ đơn giản độc lập (simple indenpendent examples)
thay vì một ví dụ chạy kết nối (connected running examples). Các ví dụ độc lập
(Independent examples) là dễ hơn để hiểu độc lập (in isolation), nhưng đưa ra ít chỉ dẫn về
cách bạn đặt chúng lại với nhau như thế nào. Một ví dụ kết nối (connected example) chỉ ra
như thế nào chúng phù hợp với nhau (how things fit together), nhưng thật khó để hiểu được
bất kỳ một pattern mà không hiểu tất cả những cái khác có liên quan trong ví dụ. Trong khi
về mặt lý thuyết nó có thể tạo ra các ví dụ được kết nối mà có thể hiểu một cách độc lập,
làm như vậy là rất khó hoặc ít nhất là quá khó đối với tôi-nên tôi đã chọn con đường độc lập.
Code trong các ví dụ được viết với việc tập trung vào việc làm cho các ý tưởng dễ hiểu. Kết
quả, several things fall aside -- , đặc biệt, erro handling (xử lý lỗi), mà tôi không quan tâm
nhiều đến vì tôi đã không phát triển bất cứ pattern ở khu vực này. Chúng hoàn toàn là để
minh họa mô hình. Chúng không có ý định để chỉ ra như thế nào để mô hình bất kỳ vấn đề
kinh doanh cụ thể.
Không phải tất cả các sections xuất hiện trong tất cả các mẫu. Nếu tôi không thể nghĩ ra
một ví dụ điển hình hoặc motivation text, tôi bỏ nó ra (I left it out).
Giới hạn của các khuôn mẫu – Limitations of These Patterns
Cuốn sách này phản ánh sự hiểu biết hiện tại của tôi, và sự hiểu biết đó đã phát triển như
tôi đã viết cuốn sách. Tôi hy vọng nó sẽ tiếp tục phát triển lâu sau khi cuốn sách đã xuất
bản. Một điều chắc chắn của sự phát triển phần mềm là nó không bao giờ đứng yên.
Khi bạn xem xét sử dụng các pattern, không bao giờ quên rằng chúng là một điểm khởi đầu,
không phải là một điểm đến cuối cùng. Không có cách nào mà bất kỳ tác giả có thể xem tất
cả các biến thể mà nhiều dự án phần mềm có. Tôi đã viết những pattern để giúp cung cấp
một khởi đầu, vì vậy bạn có thể đọc về những bài học mà tôi và những người tôi đã quan
sát, đã học được từ làm và gặp khó khăn. Luôn luôn nhớ rằng mỗi pattern là không đầy đủ
và rằng bạn có trách nhiệm, và sự vui vẻ, hoàn thành nó trong bối cảnh hệ thống của riêng
bạn.
2 Chapter 1. Layering
Layering là một trong những kỹ thuật phổ biến nhất mà các software designer sử dụng để
phá vỡ (break apart) một hệ thống phần mềm phức tạp (complicated software system).
Bạn nhìn thấy nó trong các kiến trúc máy tính (machine architectures), nơi các layers kế
thừa (descend) từ một ngôn ngữ lập trình với OS calls vào device drivers và tập lệnh
(instruction sét) CPU, và vào cổng logic (logic gates) bên trong chips. Networding có FTP
layered phía trên của TCP (on top of TCP), tầng mà ở phía trên IP (on top of IP), tầng này
phía trên của Ethernet (on top of Ethernet).
Khi suy nghĩ về một system trong terms of layers, bạn hãy tưởng tượng các principal
subsytems (hệ thống con chính) trong software được sắp xếp trong một số form của
layer cake, trong đó mỗi layer dựa trên một layer thấp hơn. Trong schema này, các
layer cao hơn sử dụng các service khác nhau được định nghĩa (define) bởi các layer
thấp hơn, nhưng các layer thấp hơn là không biết gì về các layer cao hơn. Hơn nữa,
mỗi layer thường giấu (hide) các layer thấp hơn của nó từ các layer trên, như vậy
layer 4 sử dụng các service của layer 3, layer mà sử dụng các service của layer 2, layer 4
không biết gì về layer 2. (Không phải tất cả các kiến trúc layering được mờ đục (opaque)
như thế này , nhưng hầu hết là-hay đúng hơn là hầu hết chủ yếu là opaque.
Phân tách (breaking down) một hệ thống thành các layers có một số lợi ích quan trọng:
 Bạn có thể hiểu một single layer như là một coherent whole mà không cần biết
nhiều về các layer khác. Bạn có thể hiểu làm thế nào để xây dựng một service FTP
phía trên TCP (on top of TCP) mà không biết các chi tiết xem ethernet làm việc như
thế nào.
 Bạn có thể thay thế (substitute) các layer với việc thực hiện (implementation)
thay đổi nhau (alternative) của các services basic giống nhau. Một service FTP
có thể chạy mà không có sự thay đổi qua ethernet, PPP, hoặc bất cứ một cable
company sử dụng.
 Bạn giảm thiểu sự phụ thuộc giữa các layers. Nếu các cable company thay đổi hệ
thống truyền dẫn vật lý của nó, chúng ta không cần phải thay đổi service FTP của
chúng ta.
 Các layers là nơi tốt cho chuẩn hóa (standardization). TCP và IP là tiêu chuẩn bởi vì
chúng định nghĩa các layers nên hoạt động như thế nào.
 Một khi bạn đã xây dựng một layer, bạn có thể sử dụng nó cho nhiều services mức
cao hơn (higher-level). Như vậy, TCP / IP được sử dụng bởi FTP, telnet, SSH, và
HTTP. Nếu không, tất cả các giao thức mức cao hơn (higher-level) sẽ phải viết các
giao thức mức thấp hơn (lower-level) cho chính các giao thức đó.
Layering là một kỹ thuật quan trọng, nhưng có nhược điểm (downsides):
 Các Layer gói một số 'things', nhưng không phải tất cả. Kết quả là đôi khi bạn nhận
được tầng (cascading) thay đổi. Các ví dụ điển hình của việc này trong một ứng dụng
enterprise được phân tầng (layered) được bổ sung thêm một field mà cần phải hiển
thị trên UI, phải có trong cơ sở dữ liệu, và do đó phải được thêm vào mọi layer ở
giữa.
 Các extra layers (tầng đệm) có thể gây tổn hại cho performance. Tại mỗi layers
'things' thường cần phải được chuyển đổi (transformed) từ một layer này tới layer
khác. Tuy nhiên, việc đóng gói của một chức năng underlying thường mang đến cho
bạn hiệu quả đạt được hơn là bù đắp. Một layer kiểm soát các transaction có thể được
tối ưu hóa và sau đó sẽ làm cho mọi thứ nhanh hơn.
Nhưng phần khó nhất của một kiến trúc phân tầng (layered) là quyết định xem các layers có
cái gì và trách nhiệm của mỗi layer nên là gì (what layers to have and what the
responsibility of each layer should be).
Sự phát triển (Evolution) của các Layers trong các ứng dụng Enterprise
Trong những ngày đầu (early days) của batch systems, tôi không cảm nhận được rằng
người ta nghĩ nhiều về layers trong những ngày đó. Bạn viết một chương trình để thao tác
một số form của các files (ISAM, VSAM, vv), và đó là ứng dụng của bạn. Không có layers
cần áp dụng.
Các khái niệm về layers trở nên rõ ràng hơn trong những năm 90s với sự nổi lên của các
client-server systems. Đây là hệ thống 2-layers: Các client chứa (held) UI và code
ứng dụng khác, và các servers thường là một relation database. Các tools client phổ
biển là VB, PowerBuilder, và Delphi. Các tools này làm cho việc xây dựng các ứng dụng
data-intensive trở nên dễ dàng, giống như chúng hỗ trợ UI Widget đã nhận thức (aware)
SQL. Vì vậy bạn có thể xây dựng một screen bằng cách kéo các control vào design area và
sau đó sử dụng các property sheet để kết nối các controls tới database.
Nếu ứng dụng chỉ là hiển thị và cập nhật dữ liệu relational đơn giản, thì các hệ thống client-
server làm việc rất tốt. Vấn đề đến với domain logic: các business rules, các validations, các
calculations, ... Thông thường người ta sẽ viết chúng trên client, nhưng điều này là vụng về
(awkward) và thường được thực hiện bằng cách nhúng logic trực tiếp vào UI screen. Khi
domain logic trở nên phức tạp hơn, code này trở nên rất khó khăn để làm việc. Hơn nữa,
nhúng logic vào screen làm cho nó dễ duplicate code, điều này có nghĩa rằng những thay đổi
đơn giản dẫn đến hunting down các similar code trong nhiều screens.
Một thay thế là đặt domain logic trong database giống như stored procedures. Tuy nhiên,
các store procedures ‘gave limited structuring mechanisms’, trong đó một lần nữa dẫn đến
awkard code. Ngoài ra, nhiều người thích relational database bởi vì SQL là một chuẩn đã cho
phép họ thay đổi database vendor của họ. Mặc dù thực tế rằng rất ít người thực sự đã làm
điều này, nhiều người thích có các option để thay đổi vendor mà không mất một chi phí cho
porting quá cao. Bởi vì chúng là độc quyền (all proprietary), stored procedures đã loại bỏ
option đó.
Tại cùng thời điểm mà client-server phổ biến, thế giới hướng đối tượng (object-oriented) đã
nổi lên (rising). Cộng đồng ‘object’ có một câu trả lời cho vấn đề domain miền: Di chuyển tới
một hệ thống 3-layer. Trong phương pháp này, bạn có một presentation layer cho UI của
bạn, một domain layer cho domain logic của bạn, và một data source. Bằng cách này bạn
có thể di chuyển tất cả domain logic miền ra khỏi UI và đặt nó vào một layer mà bạn có thể
cấu trúc nó đúng với các objects.
Mặc dù vậy, đối tượng bandwagon thực hiện (made) chút tiến triển. Sự thật là nhiều hệ
thống là đơn giản, hoặc ít nhất là bắt đầu theo cách đó. Và mặc dù các phương pháp 3-layer
có nhiều lợi ích, các tools cho client-server là hấp dẫn (compelling) nếu vấn đề của bạn là
đơn giản. Các tools client-server cũng gặp nhiều khó khăn, hoặc thậm chí không thể, để sử
dụng trong một cấu hình 3-layer.
Tôi nghĩ rằng các seismic shock (cú sốc lớn/địa chấn) ở đây là sự nổi lên của Web (the rise
of the Web). Đột nhiên người ta muốn triển khai các ứng dụng client-server với Web
browser. Tuy nhiên, nếu tất cả các business logic của bạn đã được buried (chôn) trong một
rich client, thì tất cả các business logic của bạn cần phải được làm lại (redone) để có Web
interface. Một hệ thống 3-layer được thiết kế tốt chỉ có thể thêm một presentation layer mới
và được thực hiện với nó. Hơn nữa, với Java, chúng ta thấy một ngôn ngữ hướng đối tượng
unashamedly nhấn chủ đạo (hit the mainstream). Các tools xuất hiện để xây dựng các Web
pages là ít hơn (much less) gắn với SQL và vì vậy nhiều trách nhiệm (amenable) tới 3rd
layer.
Khi mọi người thảo luận layering, thường có một số nhầm lẫn giữa 2 thuật ngữ: layer và
tier. Thường thì cả hai được sử dụng như là từ đồng nghĩa, nhưng hầu hết mọi người nhìn
thấy tier ngụ ý phân tách mức vật lí (physical separation). Hệ thống client-server
thường được mô tả như là hệ thống 2-tier, và sự phân tách là physical: Các client là một
desktop và server là một server. Tôi sử dụng layer để nhấn mạnh rằng bạn không cần phải
chạy các layers trên các máy khác nhau. Một layer riêng biệt của domain logic thường chạy
trên một desktop hoặc database server. Trong tình huống này, bạn có 2 nodes nhưng 3
layers riêng biệt. Với một loacl database tôi có thể chạy cả 3 layers trên một laptop duy
nhất, nhưng vẫn sẽ có 3 layers riêng biệt.
Ba Layers chính (The Three Principal Layers)
Đối với cuốn sách này, tôi đang tập trung thảo luận về kiến trúc của 3 layers chính:
presentation, domain, và data source. Bảng 1.1 tóm tắt các layers.
Presentation logic là về như thế nào để xử lý sự tương tác (how to handle) giữa user và
software. Điều này có thể đơn giản như một command-line hoặc text-based menu
system, nhưng ngày nay nó thường là một rich-client graphics UI hoặc một HTML-
based browser UI. Trách nhiệm (responsibilities) chính của các presentation layer là để
hiển thị thông tin tới user và thông dịch (interpret) các commands từ user vào các action
theo domain và data source.
Layer Responsibilities
Presentation Cung cấp các services, hiển thị thông tin (ví dụ, trong Windows hoặc
HTML), xử lí các user request (nhấp chuột, gõ keyboard), HTT[ requests,
lời gọi command-line, batch API
Domain Logic đó là real point của hệ thống
Data Source Giao tiếp với databases, messaging systems, transaction managers, và
các gói khác
Data source logic là về giao tiếp với các systems khác để thực hiện task đại diện (behalf
of) cho các ứng dụng. Đây có thể là transaction monitors, các ứng dụng khác, messaging
systems, ... Đối với hầu hết các ứng dụng enterprise phần lớn nhất của data source logic là
một database mà trách nhiệm chính của nó là lưu trữ persistent data.
Phần còn lại là domain logic, cũng được gọi là business logic. Đây là công việc (work)
mà ứng dụng cần phải làm cho các domain mà bạn đang làm việc. Nó liên quan đến việc
tính toán (calculations) dựa trên các inputs và stored data, kiểm tra (validation) dữ liệu
(any data) đến từ presentation, và tính toán (figuring out) chính xác những gì data
source logic để dispatch (what data source logic to dispatch), tùy thuộc vào các commands
nhận được từ presentation.
Đôi khi các layers được sắp xếp để domain layer hoàn toàn ẩn các data source từ
presentation. Thường xuyên hơn (more often), tuy nhiên, presentation truy xuất data store
trực tiếp. Trong khi điều này là ít pure (tinh khiết), nó có xu hướng làm việc tốt hơn trong
thực tế. Presentation có thể thông dịch (interpret) một command từ user, sử dụng data
source để kéo (pull) dữ liệu có liên quan ra khỏi database, và sau đó để cho domain logic
thao tác (manipulate) data đó trước khi trình bày (presenting) nó trên glass.
Một ứng dụng đơn lẻ (single) thường có thể có nhiều packages của mỗi một trong 3 subject
Một ứng dụng được thiết kế để được thao tác không chỉ bởi các end users thông qua một
rich-client interface mà còn thông qua một command line sẽ có 2 presentations: một cho
rich-client interface và một cho command line. Nhiều thành phần (component) data source
có thể biểu diễn (present) cho các database khác nhau, nhưng sẽ là đặc biệt (particularly)
để giao tiếp với các packages đang tồn tại. Ngay cả domain có thể được chia (broken into)
thành các khu vực riêng biệt (distinct areas) tương đối tách biệt với nhau. Một số data
source packages nhất định chỉ có thể được sử dụng bởi các domain packages nhất định.
Cho đến nay (So far) tôi đã nói về một user. Điều này đưa ra (raise) câu hỏi về điều gì xảy
ra (what happen) khi không có là một human nào điều khiển (driving) các software.
Đây có thể là một cái gì đó mới và fashionable như một Web service hoặc một cái gì đó
nhàm chán và hữu ích như một batch process. Trong trường hợp sau, user là các client
program. Tại điểm này (at this point), nó trở nên rõ ràng rằng có rất nhiều điểm giống nhau
giữa các presentation và data source layers trong đó cả hai đều là về kết nối với thế giới bên
ngoài (outside world). Đây là logic đằng sau (behind) Hexagonal Architecture pattern
của Alistair Cockburn, mà hình dung (visualizes) bất kỳ hệ thống nào giống như là một core
được bao quanh (surrounded) bởi các interfaces với các hệ thống bên ngoài (external
systems). Trong Hexagonal Architecture tất cả mọi thứ về cơ bản là outside interface, và do
đó là một quan điểm đối xứng (symmetrical view) chứ không phải là layering schema không
đối xứng của tôi.
Tôi tìm thấy bất đối xứng này hữu ích (useful), tuy nhiên, bởi vì tôi nghĩ rằng có một sự
phân tách tốt (good distinction) được thực hiện giữa một interface mà bạn cung cấp như
một service cho others và sử dụng service của người khác. Đi sâu vào phần core, đây là sự
khác biệt thực sự tôi làm giữa các presentation và data source. Presentation là một external
interface cho một service mà hệ thống của bạn cung cấp cho người khác, cho dù đó là
complex human hoặc một remote program đơn giản. Data source là interface cho những cái
(things) đang cung cấp một service cho bạn. Tôi tìm ra nó có lợi để suy nghĩ về chúng khác
nhau vì sự khác biệt trong các client làm thay đổi cách bạn suy nghĩ về service.
Mặc dù chúng ta có thể xác định được 3 responsibility layers chung của pressentation,
domain, và data source cho mỗi ứng dụng enterprise, làm thế nào bạn phân tách
(separation) chúng phụ thuộc vào độ phức tạp của ứng dụng như thế nào. Một
script đơn giản để pull data từ database và hiển thị nó trong một Web page tất cả có thể là
một procedure. Tôi vẫn sẽ nỗ lực để tách 3 layers, nhưng trong trường hợp đó tôi có thể làm
điều đó chỉ bằng cách đặt các behavior của mỗi layer trong subroutines riêng biệt. Khi
hệ thống trở nên phức tạp hơn, tôi sẽ phá vỡ (break) 3 layers thành các classes riêng
biệt. Nếu như hệ thống phức tạp tăng lên, tôi sẽ chia layers thành các package riêng
biệt. Lời khuyên chung của tôi là chọn những form thích hợp nhất của việc phân tách
(separation) cho vấn đề của bạn, nhưng chắc chắn rằng bạn làm một số loại separation, ít
nhất là ở mức subroutine.
Cùng với sự tách biệt (separation), cũng có một quy tắc ổn định (steady) về phụ thuộc: Các
domain và data source không bao giờ nên phụ thuộc vào việc presentaion. Đó là,
không nên có lời gọi subroutine từ code domain hoặc code data source tới presentaion code.
Quy tắc này làm cho nó dễ dàng hơn để thay thế các presentations khác nhau trên cùng
nền tảng và làm cho nó dễ dàng hơn để sửa đổi các presentation mà không có sự phân
nhánh (ramifications) nghiêm trọng xuống sâu hơn. Mối quan hệ giữa domain và data source
phức tạp hơn và phụ thuộc vào mô hình kiến trúc được sử dụng cho data source.
Một trong những phần khó nhất làm việc với domain logic có vẻ là mọi người thường
cảm thấy khó khăn để nhận ra domain logic là gì và các form của logic là gì. Một bài
informal test tôi thích tưởng tượng bổ sung thêm một layer hoàn toàn khác tới một ứng
dụng, chẳng hạn như một command-line inteface tới một Web application. Nếu có bất kỳ
chức năng mà bạn phải lặp lại để làm điều này, đó là một dấu hiệu 'where domain logic
has leaked into the presentation'. Tương tự như vậy, bạn có lặp logic để thay thế một
relational database với một XML file?
Một ví dụ điển hình của điều này là một hệ thống chứa một danh sách các sản phẩm mà
trong đó tất cả các sản phẩm bán được hơn 10% so với họ đã bán trong tháng trước đó sẽ
được sơn màu đỏ. Để làm được điều này các developer đặt logic trong presentation layer so
sánh với doanh số của tháng này với tháng trước và nếu lệch hơn 10%, chúng được đặt màu
đỏ.
Vấn đề là điều đó đang đặt domain logic vào presentation. Để phân tách đúng các layers,
bạn cần một method trong các domain layers để cho biết nếu một sản phẩm đã cải thiện
doanh số bán hàng. Method này thực hiện so sánh giữa hai tháng và trả về một giá trị
Boolean. Presentaion layer sau đó chỉ cần gọi method Boolean này và, nếu là true, highlights
sản phẩm màu đỏ. Bằng cách đó process được chia (broken into) thành 2 parts: quyết định
xem có cái gì đó có thể highlight và lựa chọn xem như thế nào để làm nổi bật (highlight).
Choosing Where to Run Your Layers
Nội dung chính của cuốn sách này sẽ nói về logic layers -- đó là, phân chia một hệ thống
thành phần riêng biệt (separate pieces) để giảm coupling giữa các phần khác nhau của
một hệ thống. Phân tách giữa các layers là hữu ích ngay cả khi các layers đều chạy trên một
physical machine. Tuy nhiên, there are places where the physical structure of a system
makes a difference.
Đối với hầu hết các ứng dụng IS quyết định (decision) xem là để chạy processing trên một
client, trên một desktop machine, hoặc trên một server.
Thông thường các trường hợp đơn giản nhất là chạy tất cả mọi thứ trên servers. Một HTML
front end sử dụng một Web browser là một cách tốt để làm điều này. Ưu điểm lớn nhất của
việc chạy trên server là mọi thứ đều rất dễ dàng để nâng cấp (upgrade) và sửa chữa (fix) vì
it's in a limited amount of places. Bạn không cần phải lo lắng về việc triển khai cho nhiều
desktops và giữ cho chúng tất cả đồng bộ với server. Bạn không cần phải lo lắng về tương
thích với các phần mềm desktop khác.
Lập luận chung (the general argument) ủng hộ (favor) cho việc chạy trên một client tùy
thuộc (turns on) vào responsiveness hoặc disconnected operation.. Bất kỳ logic mà
chạy trên server cần một server roundtrip để đáp ứng với bất cứ điều gì user thực hiện.
Nếu user muốn nghịch vớ vấn (fiddle with) cái gì đó và xem thông tin phản hồi ngay lập tức,
that roundtrip gets in the way. Nó cũng cần có một kết nối mạng để chạy. The network may
like to be everywhere, but as I type this it isn't at 31,000 feet. Nó có thể là ở khắp mọi nơi,
nhưng có những người muốn làm việc bây giờ mà không đợi phủ sóng không dây để đạt
Dead End Creek. Disconnected operation mang đến những thách thức đặc biệt, và tôi sợ tôi
đã quyết định đưa những chúng ra khỏi phạm vi của cuốn sách này.
Chúng ta có thể nhìn vào các tùy chọn layer by layer. Các data source khá nhiều luôn chỉ
chạy trên các server. Các trường hợp ngoại lệ là nơi bạn có thể duplicate chức năng server
vào một suitably powerful client, thông thường khi bạn muốn disconnected operation. Trong
trường hợp này thay đổi data source trên client bị mất kết nối cần phải được đồng bộ hóa với
server.
Quyết định về nơi để chạy presentation phụ thuộc chủ yếu vào loại UI mà bạn muốn. Chạy
một rich client much means chạy các presentation trên client. Chạy một Web interface much
means chạy trên server. Có những ngoại lệ, for one, remote operation của phần mềm client
(như máy chủ X trong thế giới Unix) chạy một Web server trên desktop nhưng những trường
hợp ngoại lệ là rất hiếm.
Nếu bạn đang xây dựng một hệ thống B2C, bạn không có sự lựa chọn. Bất kỳ Tom, Dick,
hoặc Harriet có thể được kết nối với máy chủ của bạn và bạn không muốn biến bất cứ ai đi
vì họ cứ khăng khăng mua thực hiện shopping online của họ với một TRS-80. Trong trường
hợp này bạn làm tất cả các xử lý trên server và cung cấp lên HTML cho browser để deal
(giao dịch). Hạn chế của bạn với các tùy chọn HTML là mỗi bit of decision cần một roundtripi
từ client đến server, và có thể làm tổn thương (hurt) sự phản hồi (responsiveness). Bạn có
thể làm giảm một trễ (some of lag) với browser scripting và downloadable appleti, nhưng
chúng làm giảm khả năng tương thích browser của bạn và có xu hướng bổ sung thêm những
vấn đề đau đầu khác. The more pure HTML you can go, the easier life is.
That ease of life is appealing even if every one of your desktops is lovingly hand-built by
your IS department. Giữ client cập nhật và tránh được các lỗi tương thích với các phần mềm
khác là các vấn đề, ngay cả hệ thống rich-client đơn giản có.
Lý do chính mà mọi người muốn có một rich-client presentation là một số nhiệm vụ khá
phức tạp cho users để làm và, để có một ứng dụng có thể sử dụng, họ sẽ cần nhiều hơn
những gì một Web GUI có thể cung cấp cho. Càng ngày, tuy nhiên, người ta thường sử dụng
các để tạo Web front ends hữu ích hơn, và làm giảm sự cần thiết cho một rich client
presentation. Khi tôi viết bài này tôi rất ủng hộ Web presentation nếu bạn có thể và rich
client nếu bạn phải làm.
Điều này để lại (leave) cho chúng ta với domain logic. Bạn có thể chạy các business logic tất
cả trên server hoặc tất cả trên client, hoặc bạn có thể chia nó. Một lần nữa, tất cả trên
server là sự lựa chọn tốt nhất để dễ bảo trì. Nhu cầu để di chuyển nó đến các client là cho
hoặc responsiveness hoặc cho disconnected use.
Nếu bạn phải chạy một số logic trên client, bạn có thể xem xét việc chạy tất cả nó ở đó -- at
least that way it's all in one place. Thông thường điều này đi kèm (hand in hand) với một
rich client -- đang chạy một Web server trên một client machine không phải để giúp
responsiveness hơn, mặc dù nó có thể là một cách để đối phó với disconnected operation.
Trong trường hợp này, bạn vẫn có thể giữ domain logic của bạn trong các modules riêng biệt
từ presentation, với một Transaction Script hoặc một Domain Model. Vấn đề với việc đưa tất
cả các domain logic trên client là bạn phải upgrade và maintain nhiều hơn.
Splitting across cả desktop và server nghe giống như the worst of both worlds bởi vì bạn
không biết where any piece of logic may be. Lý do chính để làm điều đó là bạn chỉ có một số
lượng nhỏ của domain logic mà cần phải chạy trên client. Mẹo (trick) đó là cô lập (isolate)
phần logic này trong một module khép kín (self-contained) mà không phụ thuộc vào bất kỳ
một phần khác của hệ thống. Bằng cách đó bạn có thể chạy mà module trên client hoặc
server.
Một khi bạn đã chọn các nút xử lý của bạn (processing node), bạn nên cố gắng giữ tất cả
code trong một single process, hoặc một node hoặc copied trên một vài nodes trong một
cluster. Đừng cố gắng để tách các layers vào cá discrete (rời rạc) processes, trừ khi bạn phải
làm. Làm như vậy sẽ làm giảm hiệu suất (degrade performance) và thêm phức tạp (add
performance), như bạn phải thêm những thứ như Remote Facades và Data Transfer Objects.
Điều quan trọng là phải nhớ rằng nhiều cái trong số những điều này là những gì Jens
Coldewey gọi là phức tạp -- distribution, explicit multithreading, paradigm chasms (như
object / relational), multiplatform development, và các yêu cầu extreme performance (như
hơn 100 transactions/s ). Tất cả chúng đưa tới high cost (chi phí cao). Chắc chắn có những
lúc bạn phải làm điều đó, nhưng không bao giờ quên rằng mỗi một cái mang một chi phí
(charge) trong phát triển và bảo trì liên tục (on-going maintenance).
3 Chapter 2. Organizing Domain Logic
Trong tổ chức Domain Logic, tôi đã phân tách nó thành 3 patterns chính: Transaction
Script, Domain Model, và Table Module.
Phương pháp đơn giản nhất để lưu domain logic là Transaction Script. Một Transaction Script
bản chất là một thủ tục (procedures) lấy đầu vào từ presentation, xử lý nó với validations và
calculations, lưu dữ liệu trong database, và gọi (invokes) các hoạt động bất kì từ các hệ
thống khác. Sau đó nó trả lời (replies) với nhiều dữ liệu tới presentation, có lẽ thực hiện tính
toán nhiều hơn để giúp tổ chức (organize) và định dạng (format) dữ liệu trả về (reply). Các
tổ chức cơ bản (fundamental organization) là một thủ tục duy nhất (single procedure) cho
mỗi action mà một user có thể muốn làm. Do đó, chúng ta có thể nghĩ về mô hình này như
là một script cho một action, hoặc business transaction. Nó không phải là một single
inline procedure của code. Các phần (pieces) được tách ra thành các chương trình con
(subroutines), và các subroutines có thể được chia sẻ giữa Transaction Scripts khác nhau.
Tuy nhiên, driving force vẫn là của một procedure cho mỗi action, do đó, một hệ thống bán
lẻ có thể có Transaction Scripts cho checkout, cho adding something vào giỏ mua hàng, để
hiển thị trạng thái giao hàng, ...
Một Transaction Script có nhiều ưu điểm:
 Đó là một mô hình thủ tục (procedural model) đơn giản mà hầu hết các developer
hiểu.
 Nó hoạt động tốt với một data source layer đơn giản bằng cách sử dụng Row Data
Gateway hoặc Table Data Gateway.
 Rõ ràng (obvious) làm thế nào để thiết lập các ranh giới giao dịch (transaction
boundaries): Bắt đầu với việc mở một transaction và kết thúc với đóng cửa nó. Thật
dễ dàng cho các tools để làm điều này đằng sau các scenes.
Đáng buồn thay, Transaction Script cũng có nhiều nhược điểm, có xu hướng xuất hiện khi sự
phức tạp của domain logic tăng. Thông thường code sẽ bị duplicated khi một số transactions
cần làm những cái tương tự nhau (Often there will be duplicated code as several transactions
need to do similar things). Một số (some of this) có thể được xử lý bằng cách tái cấu trúc
(factoring out) ra các hàm chung (common subroutines), nhưng ngay cả như vậy nhiều
trùng lặp code (duplication) là khó khăn để loại bỏ và khó nhận ra hơn. Ứng dụng đạt được
(resulting application) có thể trở thành (end up) là một web khá rối với các routines không
có một cấu trúc rõ ràng.
Tất nhiên, logic phức tạp là nơi các objects đi vào (complex logic is where objects come in),
và phương pháp (way) object-oriented để xử lý vấn đề này là với một Domain Model. Với
một Domain Model, chúng tôi xây dựng một model của domain trong đó, ít nhất on a first
approximation, được tổ chức chủ yếu xung quanh các danh từ trong domain. Do đó, một hệ
thống cho thuê (leasing system) sẽ có các classes cho lease, asset, và ... Logic để
validations và calculations sẽ được đặt vào domain model này, vì vậy đối tượng shipment có
thể chứa các logic để tính toán shipping charge cho một delivery. Có thể vẫn là các rountines
cho tính toán một hóa đơn, nhưng như vậy (but such) một procedure sẽ nhanh chóng
delegate tới một method Domain Model.
Sử dụng một Domain Model trái ngược với một Transaction Script là the essence of the
paradigm shift that object-oriented people talk about so much. Thay vì một routine có tất cả
các logic cho một action người dùng, mỗi object có một phần logic liên quan đến nó. Nếu
bạn không sử dụng đến một Domain Model, học để làm việc với một cái có thể rất bực bội
khi bạn vội vàng từ object tới object cố gắng tìm xem các behavior ở đâu (If you're not used
to a Domain Model, learning to work with one can be very frustrating as you rush from
object to object trying to find where the behavior is).
Thật khó để nắm bắt được bản chất của sự khác biệt giữa hai patterns với một ví dụ đơn
giản, nhưng trong thảo luận về các patterns tôi đã cố gắng để làm điều đó bằng cách xây
dựng một pieces đơn giản của domain logic theo cả hai cách. Cách dễ nhất để thấy sự khác
biệt là nhìn vào sequence diagrams cho hai phương pháp tiếp cận (hình 2.1 và 2.2). Vấn đề
quan trọng là các loại product khác nhau có các thuật toán khác nhau để recognizing
revenue trên một contract được đưa ra. Các method tính toán để xác định xem những loại
product một contract được đưa ra là gì, cho việc áp dụng các thuật toán chính xác, và sau đó
tạo ra các revenue recognition objects để nắm bắt được kết quả của phép tính. (Để đơn giản
tôi bỏ qua các vấn đề tương tác cơ sở dữ liệu.)
Trong hình 2.1, phương thức Transaction Script thực hiện mọi việc. Các đối tượng cơ sở
(underlying objects) là Table Data Gateway, và tất cả chúng làm là pass data tới transaction
script.
Ngược lại, hình 2.2 cho nhiều đối tượng, từng phần chuyển tiếp (forwarding part) của
behavior tới phần khác cho đến khi một strategy object tạo ra các kết quả.
Giá trị của một Domain Model nằm trong thực tế rằng một khi bạn đã quen với điều đó, có
rất nhiều kỹ thuật cho phép bạn xử lý logic ngày càng phức tạp theo một cách được tổ chức
tốt. Khi chúng ta nhận được (get) nhiều hơn và nhiều hơn nữa các thuật toán để tính toán
revenue recognition, chúng ta có thể thêm những cái này bằng cách thêm vào các đối tượng
recognition strategy mới. Với Transaction Script, chúng ta đang bổ sung thêm nhiều điều
kiện hơn tới các conditional logic của script.
Một khi tâm trí bạn là 'warped to objects' như tôi, bạn sẽ thấy bạn thích một Domain Model
ngay cả trong trường hợp khá đơn giản.
Các chi phí của Domain Model đến từ sự phức tạp của việc sử dụng nó và sự phức tạp của
data source layer của bạn. Phải mất thời gian cho những người mới tới các rich obect models
để làm quen với một một rich Domain Model. Thường thì developers có thể phải bỏ ra nhiều
tháng làm việc trên một dự án có sử dụng pattern này trước khi paradigms của họ được thay
đổi. Tuy nhiên, khi bạn đã quen với Domain Model bạn thường infected (suy luận) cho cuộc
sống và nó trở nên dễ dàng để làm việc trong tương lai. Tuy nhiên, một thiểu số đáng kể của
các developers dường như là không thể thực hiện việc chuyển đổi.
Ngay cả khi bạn đã thực hiện các thay đổi, bạn vẫn phải giải quyết (deal) với database
mapping. The richer your Domain Model, the more complex your mapping to a
relational database (usually with Data Mapper). Một data source layer phức tạp giống
như một chi phí cố định – phải mất một số tiền (nếu bạn mua) hoặc thời gian (nếu bạn xây
dựng), nhưng một khi bạn có nó, bạn có thể làm rất nhiều với nó.
Có một lựa chọn thứ ba cho logic miền cấu trúc, Table Module. Với cái nhìn đầu tiên Table
Module trông giống như một Domain Model vì cả hai đều có các classes cho các contracts,
các products, và các revenue recognitions. Sự khác biệt quan trọng là một Domain Model có
một instance của contract cho mỗi contract trong database trong khi một Table Module chỉ
có một instance. Một Table Module được thiết kế để làm việc với một Record Set. Vì vậy,
client của một contract Table Module đầu tiên sẽ phát hành (issue) các truy vấn tới database
để tạo thành một Record Set và sẽ tạo ra một contract object và truyền (pass) nó Record
Set như một đối số. Client có thể gọi các hoạt động về contract để làm những việc khác
nhau (Hình 2.3). Nếu nó muốn làm một cái gì đó tới một contract riêng lẻ, nó phải truyền
(pass) một ID.
Một Table Module (trong nhiều cách) là một middle ground giữa một Transaction Script và
một Domain Model. Tổ chức domain logic xung quanh các tables hơn là các straight
procedures cung cấp cấu trúc hơn (more structure) và làm cho nó dễ dàng hơn để tìm và
loại bỏ trùng lặp. Tuy nhiên, bạn không thể sử dụng một số kỹ thuật mà một Domain Model
sử dụng cho cấu trúc 'finer grained' của logic, chẳng hạn như inheritance, strategies, và các
patterns OO khác.
Ưu điểm lớn nhất của một Table Module là làm thế nào nó phù hợp với phần còn lại của kiến
trúc. Nhiều môi trường GUI được xây dựng để làm việc trên các kết quả của một truy vấn
SQL được tổ chức trong một Record Set. Kể từ khi một Table Module cũng hoạt động trên
một Record Set, bạn có thể dễ dàng chạy một truy vấn, thao tác các kết quả trong Table
Module, và truyền các dữ liệu thao tác tới GUI để hiển thị. Bạn cũng có thể sử dụng Table
Module trên way back cho validations và calculations. Một số nền tảng, đặc biệt là COM và
.NET của Microsoft, sử dụng phong cách phát triển này.
Making a Choice
Làm thế nào để bạn lựa chọn giữa 3 patterns? Nó không phải là một lựa chọn dễ dàng, và nó
phụ thuộc rất nhiều vào độ phức tạp domain logic của bạn thế nào. Hình 2.4 là một trong
những đồ thị non-scientific mà thực sự kích thích tôi trong các bài thuyết trình PowerPoint.
Tuy nhiên, nó giúp hình dung như thế nào so sánh 3 patterns. Với domain logic đơn giản
Domain Model là kém hấp dẫn vì chi phí cho việc tìm hiểu và sự phức tạp của data source
thêm rất nhiều effort để phát triển nó sẽ không được trả lại. Tuy nhiên, khi sự phức tạp của
domain logic tăng lên, các phương pháp tiếp cận khác có xu hướng 'hit a wall' ở đó (where)
thêm nhiều tính năng hơn theo cấp số nhân trở nên khó khăn hơn.
Vấn đề của bạn, tất nhiên, là tìm ra ứng dụng của bạn nằm ở đâu trên x-axis. Tin tốt là tôi
có thể nói rằng bạn nên sử dụng một Domain Model bất cứ khi nào sự phức tạp của domain
logic của bạn là lớn hơn 7,42. Tin xấu là không ai biết làm thế nào để đo lường mức độ phức
tạp của domain logic. Trong thực tế, tất cả bạn có thể làm là tìm thấy một số người có kinh
nghiệm những người có thể làm một phân tích ban đầu các yêu cầu và thực hiện một
judgment call.
Có một số yếu tố làm thay đổi đường cong một chút. Một nhóm quen thuộc với Domain
Model chi phải ban đầu sử dụng pattern này sẽ thấp hơn. The better the team is, the more
I'm inclined to use a Domain Model.
Sự hấp dẫn của một Table Module phụ thuộc rất nhiều vào sự hỗ trợ cho một cấu trúc
Record Set common trong môi trường của bạn. Nếu bạn có một môi trường như .NET hoặc
Visual Studio, nơi mà rất nhiều công cụ làm việc xung quanh một Record Set, thì điều đó
làm Table Module hấp dẫn hơn nhiều. Thật vậy, tôi không thấy có lý do để sử dụng
Transaction Scripts trong một môi trường .NET. Tuy nhiên, nếu không có tools đặc biệt cho
Record Sets, tôi sẽ không bận tâm với Table Module.
Một khi bạn đã thực hiện nó, quyết định của bạn không hoàn toàn được đúc bằng đá (cast in
stone), nhưng nó là khó khăn hơn để thay đổi. So it's worth some upfront thought to decide
which way to go. Nếu bạn nhận thấy bạn đã đi sai đường, thì, nếu bạn đã bắt đầu với
Transaction Script (110), không ngần ngại cấu trúc lại theo hướng Domain Model. Nếu bạn
đã bắt đầu với Domain Model, tuy nhiên, đi tới (going to) Transaction Script thường ít đáng
giá, trừ khi bạn có thể đơn giản hóa data source layer của bạn.
These three patterns are not mutually exclusive choices. Thật vậy, nó khá phổ biến để sử
dụng Transaction Script cho một số domain logic và Table Module hoặc Domain Model cho
phần còn lại.
Service Layer
Một cách tiếp cận phổ biến trong xử lý domain logic là phân chia các domain layer thành 2
(in two). Một Service Layer được đặt trên (placed over) một Domain Model cơ bản hoặc
Table Module. Thông thường, bạn chỉ nhận được điều này với một Domain Model hoặc Table
Module khi (since) một domain layer mà chỉ sử dụng Transaction Script là không đủ phức
tạp để đảm bảo một layer riêng biệt. Presentation logic tương tác với domain hoàn toàn
thông qua Service Layer, hoạt động như một API cho ứng dụng.
Cũng như cung cấp một API rõ ràng, Service Layer cũng là một điểm tốt (good spot) để đặt
những thứ như transaction control và security. Điều này cung cấp cho bạn một model đơn
giản của taking each method trong Service Layer và mô tả đặc điểm transaction và security
của nó. Một file thuộc tính riêng biệt là một lựa chọn phổ biến cho điều này, nhưng .NET
attributes cung cấp một cách tốt làm trực tiếp trong code.
Khi bạn nhìn thấy một Service Layer, một quyết định quan trọng là có bao nhiêu behavior
đặt vào nó (how many behavior to put in it). Trường hợp tối thiểu là làm cho Service Layer
một facade để tất cả các real behavior là trong underlying objects và tất cả các Service
Layer làm là chuyển tiếp cuộc gọi (calls) trên facade tới các object mức thấp hơn. Trong
trường hợp đó các Service Layer cung cấp một API dễ dàng hơn để sử dụng vì nó thường
hướng đến xung quanh các use cases. Nó cũng làm cho một điểm thuận tiện cho việc thêm
transactional wrappers và kiểm tra security.
Ở một thái cực khác (other extreme), hầu hết các business logic được đặt trong Transaction
Scripts bên trong các Service Layer. Các underlying domain objects rất đơn giản; nếu đó là
một Domain Model nó sẽ là one-to-one với database và do đó bạn có thể sử dụng một data
source layer đơn giản hơn như Active Record.
Midway between these alternatives is a more even mix of behavior: the controller-entity
style. Tên này xuất phát từ một thực tế phổ biến ảnh hưởng nặng nề bởi [Jacobson et al.].
Vấn đề ở đây là có logic đặc biệt cho một transaction duy nhất hoặc use case đặt trong
Transaction Scripts, thường được referred tới như các controllers hoặc services. Đây là các
controllers khác nhau với input controller trong Model View Controller hoặc Application
Controller (These are different controllers to the input controller in Model View Controller or
Application Controller) mà chúng ta sẽ gặp sau này, vì vậy tôi sử dụng thuật ngữ use-case
controller. Behavior đó được sử dụng trong nhiều use case đi vào các domain objects, được
gọi là entities.
Mặc dù phương pháp controller-entity là một phương pháp phổ biến, nó không phải là một
phương pháp mà tôi đã từng thích nhiều. Các use case controllers, giống như bất kỳ
Transaction Script giao dịch, có xu hướng ủng hộ (encourage) duplicate code. Quan điểm
của tôi là, nếu bạn quyết định sử dụng một Domain Model ở tất cả, bạn thực sự nên go
whole hog (cắt ngắn) and make it dominant (trọng yếu). Một ngoại lệ là nếu bạn đã bắt đầu
với một thiết kế có sử dụng Transaction Script với Row Data Gateway. Sau đó, nó tạo cảm
giác move duplicated behavior tới các Row Data Gateways, mà sẽ biến (turn) chúng thành
một Domain Model đơn giản sử dụng Active Record. Tuy nhiên, tôi sẽ không bắt đầu theo
cách đó. Tôi sẽ chỉ làm điều đó để cải thiện thiết kế mà đang có dấu hiệu cracks.
Tôi nói không phải là bạn không bao giờ nên có các service objects có chứa business logic,
nhưng mà bạn không nên nhất thiết phải làm cho một layer cố định của chúng. Các
procedural service objects đôi khi có thể là một cách rất hữu ích để factor logic, nhưng tôi có
xu hướng sử dụng chúng khi cần thiết chứ không phải là một architectural layer.
Sự ưa thích của tôi là có các Service Layer mỏng nhất (the thinnest) bạn có thể, nếu bạn
thậm chí cần một. Cách tiếp cận thông thường của tôi là giả sử tôi không cần một và chỉ
thêm nó nếu có vẻ ứng dụng cần đến nó. Tuy nhiên, tôi biết nhiều nhà thiết kế tốt, những
người luôn luôn sử dụng một Service Layer với một fair bit of logic. Randy Stafford đã có rất
nhiều thành công với một rich Service Layer, đó là lý do tại sao tôi yêu cầu ông viết Service
Layer pattern cho cuốn sách này.

More Related Content

Similar to Patterns of enterprise application architecture_VN_Drapt

Data_Warehouse
Data_WarehouseData_Warehouse
Data_WarehouseThang Luu
 
Ddd quickly-vietnamese
Ddd quickly-vietnameseDdd quickly-vietnamese
Ddd quickly-vietnamesesamazeno1
 
Giao trinh phan tich thiet ke he thong thong tin
Giao trinh phan tich thiet ke he thong thong tinGiao trinh phan tich thiet ke he thong thong tin
Giao trinh phan tich thiet ke he thong thong tinNguyen Patrick
 
phan tich thiet ke he thong
phan tich thiet ke he thongphan tich thiet ke he thong
phan tich thiet ke he thongvantinhkhuc
 
Kiến trúc ứng dụng trong doanh nghiệp – IT205
Kiến trúc ứng dụng trong doanh nghiệp – IT205Kiến trúc ứng dụng trong doanh nghiệp – IT205
Kiến trúc ứng dụng trong doanh nghiệp – IT205Nguyễn Đức
 
Phan Tich Httt Bang Uml
Phan Tich Httt Bang UmlPhan Tich Httt Bang Uml
Phan Tich Httt Bang Umlhbgfd
 
Phương pháp phát triển phần mềm: Truyền thống và Agile
Phương pháp phát triển phần mềm: Truyền thống và AgilePhương pháp phát triển phần mềm: Truyền thống và Agile
Phương pháp phát triển phần mềm: Truyền thống và AgileVu Hung Nguyen
 
8268 trinh chieu_r9c_fs_20130412110709_3
8268 trinh chieu_r9c_fs_20130412110709_38268 trinh chieu_r9c_fs_20130412110709_3
8268 trinh chieu_r9c_fs_20130412110709_3saophaiyeuai
 
Dynamo: Amazon’s Highly Available Key-value Store
Dynamo: Amazon’s Highly Available Key-value StoreDynamo: Amazon’s Highly Available Key-value Store
Dynamo: Amazon’s Highly Available Key-value StoreViet-Trung TRAN
 
Các vấn đề CNTT y tế bệnh viện.pptx
Các vấn đề CNTT y tế bệnh viện.pptxCác vấn đề CNTT y tế bệnh viện.pptx
Các vấn đề CNTT y tế bệnh viện.pptxPhan Trung
 
Baocao ltm
Baocao ltmBaocao ltm
Baocao ltmptlong96
 
the real-time operating system and real-time programming
the real-time operating system and real-time programmingthe real-time operating system and real-time programming
the real-time operating system and real-time programmingDucLe868608
 
Báo cáo kĩ thuật phần mềm và ứng dụng
Báo cáo kĩ thuật phần mềm và ứng dụngBáo cáo kĩ thuật phần mềm và ứng dụng
Báo cáo kĩ thuật phần mềm và ứng dụngVượng Đặng
 

Similar to Patterns of enterprise application architecture_VN_Drapt (20)

Data_Warehouse
Data_WarehouseData_Warehouse
Data_Warehouse
 
Đề tài: Thiết kế hệ thống thông tin phân hệ kế toán tiền lương, 9đ
Đề tài: Thiết kế hệ thống thông tin phân hệ kế toán tiền lương, 9đĐề tài: Thiết kế hệ thống thông tin phân hệ kế toán tiền lương, 9đ
Đề tài: Thiết kế hệ thống thông tin phân hệ kế toán tiền lương, 9đ
 
Cloud computing
Cloud computingCloud computing
Cloud computing
 
Ddd quickly-vietnamese
Ddd quickly-vietnameseDdd quickly-vietnamese
Ddd quickly-vietnamese
 
Đề tài: Chương trình quản lý cho thuê nhà của cơ sở dịch vụ, HOT
Đề tài: Chương trình quản lý cho thuê nhà của cơ sở dịch vụ, HOTĐề tài: Chương trình quản lý cho thuê nhà của cơ sở dịch vụ, HOT
Đề tài: Chương trình quản lý cho thuê nhà của cơ sở dịch vụ, HOT
 
Giao trinh phan tich thiet ke he thong thong tin
Giao trinh phan tich thiet ke he thong thong tinGiao trinh phan tich thiet ke he thong thong tin
Giao trinh phan tich thiet ke he thong thong tin
 
phan tich thiet ke he thong
phan tich thiet ke he thongphan tich thiet ke he thong
phan tich thiet ke he thong
 
Kiến trúc ứng dụng trong doanh nghiệp – IT205
Kiến trúc ứng dụng trong doanh nghiệp – IT205Kiến trúc ứng dụng trong doanh nghiệp – IT205
Kiến trúc ứng dụng trong doanh nghiệp – IT205
 
Phan Tich Httt Bang Uml
Phan Tich Httt Bang UmlPhan Tich Httt Bang Uml
Phan Tich Httt Bang Uml
 
Phương pháp phát triển phần mềm: Truyền thống và Agile
Phương pháp phát triển phần mềm: Truyền thống và AgilePhương pháp phát triển phần mềm: Truyền thống và Agile
Phương pháp phát triển phần mềm: Truyền thống và Agile
 
Cơ sở lý luận về phân tích, thiết kế hệ thống thông tin quản lí bán hàng.docx
Cơ sở lý luận về phân tích, thiết kế hệ thống thông tin quản lí bán hàng.docxCơ sở lý luận về phân tích, thiết kế hệ thống thông tin quản lí bán hàng.docx
Cơ sở lý luận về phân tích, thiết kế hệ thống thông tin quản lí bán hàng.docx
 
1 gioi thieu httt
1 gioi thieu httt1 gioi thieu httt
1 gioi thieu httt
 
8268 trinh chieu_r9c_fs_20130412110709_3
8268 trinh chieu_r9c_fs_20130412110709_38268 trinh chieu_r9c_fs_20130412110709_3
8268 trinh chieu_r9c_fs_20130412110709_3
 
Cơ sở lý luận về phân tích thiết kế hệ thống thông tin quản lý khách hàng.docx
Cơ sở lý luận về phân tích thiết kế hệ thống thông tin quản lý khách hàng.docxCơ sở lý luận về phân tích thiết kế hệ thống thông tin quản lý khách hàng.docx
Cơ sở lý luận về phân tích thiết kế hệ thống thông tin quản lý khách hàng.docx
 
Dynamo: Amazon’s Highly Available Key-value Store
Dynamo: Amazon’s Highly Available Key-value StoreDynamo: Amazon’s Highly Available Key-value Store
Dynamo: Amazon’s Highly Available Key-value Store
 
Tshoot module1
Tshoot module1Tshoot module1
Tshoot module1
 
Các vấn đề CNTT y tế bệnh viện.pptx
Các vấn đề CNTT y tế bệnh viện.pptxCác vấn đề CNTT y tế bệnh viện.pptx
Các vấn đề CNTT y tế bệnh viện.pptx
 
Baocao ltm
Baocao ltmBaocao ltm
Baocao ltm
 
the real-time operating system and real-time programming
the real-time operating system and real-time programmingthe real-time operating system and real-time programming
the real-time operating system and real-time programming
 
Báo cáo kĩ thuật phần mềm và ứng dụng
Báo cáo kĩ thuật phần mềm và ứng dụngBáo cáo kĩ thuật phần mềm và ứng dụng
Báo cáo kĩ thuật phần mềm và ứng dụng
 

Patterns of enterprise application architecture_VN_Drapt

  • 1. Patterns of Enterprise Application Architecture By Martin Fowler, David Rice, Matthew Foemmel, Edward Hieatt, Robert Mee, Randy Stafford
  • 2. Table of Contents 1 Giới thiệu chung..................................................................................................................3 2 Chapter 1. Layering...........................................................................................................14 3 Chapter 2. Organizing Domain Logic....................................................................................22
  • 3. 1 Giới thiệu chung Architecture 'Kiến trúc' (Architecture) là một thuật ngữ mà rất nhiều người cố gắng để định nghĩa. Có 2 yếu tố chung (common elements):  Một là sự phân chia (breakdown) của một hệ thống thành các thành phần (parts) của hệ thống đó ở mức cao nhất (highest-level).  Yếu tố khác là, các quyết định là rất khó thay đổi (decisions that are hard to change). Không chỉ có một cách để phát biểu (state) kiến trúc của một hệ thống; đúng hơn, có nhiều kiến trúc trong một hệ thống, và quan điểm/cách nhìn nhận xem tầm quan trọng kiến trúc là cái gì (the view of what is architecturally significant) là một yếu tố có thể thay đổi trong suốt lifetime của một hệ thống. Trong một bài viết, Ralph Johnson đưa ra quan điểm rằng architecture là một cái gì đó chủ quan (subjective thing), một sự chia sẻ hiểu biết (shared understanding) về thiết kế của một hệ thống bởi các chuyên gia phát triển trong một dự án. Thông thường, việc chia sẻ hiểu biết này theo dạng các thành phần chính của hệ thống và cách chúng tương tác với nhau (major components of the system and how they interact). Cũng về các quyết định (decisions), các developer mong muốn các quyết định của họ có thể đúng ngay từ sớm vì họ nhận thức rằng nó khó để thay đổi. Cuốn sách này trình bày về các thành phần chính (major parts) của ứng dụng enterprise và các quyết định (decisions) có thể nhận được ngay từ sớm. Tập trung vào kiến trúc phân tầng (layer). Và trình bày về cách chia một hệ thống enterprise vào các layers và cách các layer làm việc với nhau (how you decompose an enterprise application into layers and how these layers work together). Hầu hết các ứng dụng nontrivial enterprise sử dụng kiến trúc layers theo một số dạng, nhưng trong một số tình huống tiếp cận khác, chẳng hạn như pipes hay filters, có giá trị (valuable). Ứng dụng Enterprise Rất nhiều người viết phần mềm máy tính (computer software), và chúng ta gọi tất cả là phát triển phần mềm (software development). Tuy nhiên, có nhiều loại phần mềm khác nhau, mỗi loại đều có thách thức và sự phức tạp riêng. Điều này xuất phát ra khi tôi nói chuyện với một số bạn bè của tôi trong lĩnh vực viễn thông. Trong một số cách, ứng dụng enterprise là dễ hơn nhiều so với các phần mềm viễn thông-chúng ta không có vấn đề xử lý đa luồng rất khó khăn, và chúng ta không phải tích hợp phần cứng và phần mềm. Nhưng theo những cách khác nó khó hơn nhiều. Các ứng dụng enterprise thường có dữ liệu phức tạp (complex data) – và rất nhiều, cùng với các business rule mà 'fail all tests of logical reasoning. Mặc dù một số techiniques và patterns phù hợp cho tất cả các loại phần mềm, nhưng nhiều cái chỉ thích hợp cho duy nhất một ngành cụ thể (particular branch).
  • 4. Các patterns trong cuốn sách này tập trung để giải quyết các vấn đề cho các ứng dụng enterprise. Các thuật ngữ khác cho các ứng dụng enterprise bao gồm "các hệ thống thông tin (information systems)" hoặc, "xử lý dữ liệu (data processing)". Ta sẽ bắt đầu với các ví dụ. Ứng dụng Enterprise bao gồm payroll, patient records, shipping tracking, cost analysis, credit scoring, insurance, supply chain, accounting, customer sevice, và kinh doanh ngoại hối. Ứng dụng Enterprise không bao gồm tiêm automobile fuel injection, xử lý văn bản, điều khiển thang máy, điều khiển nhà máy hóa chất, thiết bị chuyển mạch điện thoại, hệ điều hành, các trình biên dịch, và các trò chơi. Ứng dụng Enterprise thường liên quan đến persistent data. Các dữ liệu là persistent bởi vì nó cần là di chuyển vòng quanh (around) giữa nhiều chương trình chạy – trên thực tế, nó thường cần persist trong nhiều năm. Cũng trong thời gian này sẽ có rất nhiều thay đổi trong các chương trình sử dụng nó. Nó thường sẽ tồn tại lâu hơn (outlast) các phần cứng (hardware), các hệ điều hành (OS) và các trình biên dịch (compilers). Trong thời gian đó sẽ có nhiều thay đổi về cấu trúc dữ liệu (the structure of the data) để lưu trữ các phần mới (new pieces) của thông tin mà không làm phiền (disturbing) các phần cũ (old pieces). Thậm chí nếu có một thay đổi cơ bản và các công ty cài đặt một ứng dụng hoàn toàn mới để xử lý một công việc, các dữ liệu phải được di chuyển (migrated) đến các ứng dụng mới. Thường có rất nhiều dữ liệu (a lot of data) -- một hệ thống vừa phải sẽ có hơn 1 GB dữ liệu được tổ chức trong hàng chục triệu bản ghi (records) – quản lý data là một phần quan trọng (major part) của hệ thống. Các hệ thống cũ sử dụng các cấu trúc tập tin chỉ mục (indexed file structures) như IBM VSAM và ISAM. Các hệ thống hiện đại thường sử dụng cơ sở dữ liệu (database), phần lớn cơ sở dữ liệu quan hệ (relational database). Việc thiết kế và feeding các cơ sở dữ liệu đã biến thành một subprofession của riêng nó. Thông thường có nhiều người đồng thời (concurrently) truy cập vào dữ liệu. Đối với nhiều hệ thống số người truy cập có thể ít hơn một 100 người, nhưng đối với các hệ thống Web- based qua Internet số người truy cập tăng lên theo bậc (orders of magnitude). Đối với quá nhiều người, có những vấn đề nhất định (definite issues) trong việc đảm bảo rằng tất cả người dùng có thể truy cập vào hệ thống đúng cách. Nhưng ngay cả khi không có quá nhiều người, vẫn còn có những vấn đề trong việc đảm bảo rằng hai người không truy cập vào cùng một dữ liệu tại cùng thời điểm trong một cách mà gây ra lỗi. Các công cụ quản lý transaction xử lý một số gánh nặng (burden) này, nhưng thường nó không thể che giấu (hide) điều này từ các nhà phát triển ứng dụng. Với rất nhiều dữ liệu, thường có nhiều UI screens để xử lí nó. Nó không phải bất thường khi có tới hàng trăm screen riêng biệt. Người sử dụng các ứng dụng enterprise khác nhau (vary) từ thỉnh thoảng occasional tới thường xuyên (regular), và thông thường họ sẽ có ít chuyên môn kỹ thuật. Do đó, các dữ liệu đã được trình bày nhiều cách khác nhau cho các mục đích khác nhau. Các hệ thống thường có rất nhiều (batch processing), cái dễ để quên khi tập trung vào use case mà bắt buộc (stress) sự tương tác của người dùng.
  • 5. Các ứng dụng Enterprises hiếm khi sống trên một 'hòn đảo' (island). Thông thường, chúng cần phải tích hợp với các ứng dụng enterprise khác nằm rải rác xung quanh enterprise. Các hệ thống khác nhau được xây dựng vào những thời điểm khác nhau, có technologies khác nhau, và thậm chí cả các cơ chế hợp tác (collaboration mechanisms) sẽ khác nhau: các file dữ liệu COBOL, CORBA, messaging systems. Vì vậy thông thường các enterprise sẽ cố gắng để tích hợp với các hệ thống khác nhau của nó bằng cách sử dụng một công nghệ communication chung (common communication technology). Tất nhiên, hầu như không bao giờ kết thúc job, vì thế, có một vài unified integration schemes khác nhau cùng một lúc. Điều này thậm chí tồi tệ hơn khi các enterprise tìm cách tích hợp với các đối tác kinh doanh của họ. Ngay cả khi một công ty hợp nhất các công nghệ cho integration, họ gặp vấn đề với sự khác biệt trong quá trình kinh doanh (business process) và sự bất hòa khái niệm (conceptual dissonance) với các dữ liệu. Một bộ phận của công ty có thể nghĩ rằng một customer là một người có một current agreement; bộ phận khác cũng đếm những người đã có một hợp đồng nhưng không làm được nữa (don't any longer); bộ phận khác đếm product sales nhưng không service sales. Điều đó nghe có vẻ dễ dàng, nhưng khi bạn có hàng trăm records, trong đó mỗi field có thể có một ý nghĩa khác nhau, kích thước tuyệt đối (sheer size) của vấn đề sẽ trở thành một thách thức -- ngay cả nếu người duy nhất biết field này thực sự có nghĩa là gì với công ty. (Và, tất nhiên, tất cả điều này thay đổi mà không báo trước). Kết quả là, dữ liệu phải được liên tục đọc, munged, và viết bằng tất cả các loại định dạng cú pháp và ngữ nghĩa khác nhau. Sau đó có các vấn đề của những cái đi kèm theo thuật ngữ "business logic". Khi bạn xây dựng một hệ điều hành bạn cố gắng để giữ cho tất cả mọi thứ (whole thing) hợp lý. Nhưng các business rules được đưa cho bạn, và không có major political effort không có gì bạn có thể làm gì để thay đổi chúng. Bạn phải đối phó với một mảng lộn xộn (haphazard array) của các điều kiện kỳ lạ mà chúng thường tương tác với nhau theo các cách đáng ngạc nhiên (surprising ways). Tất nhiên, họ nhận được lộ trình đó (that way) đó vì một lý do: Một số nhân viên bán hàng đàm phán để có một khoản thanh toán hàng năm nhất định muộn hơn bình thường do đó phù hợp với chu kỳ kế toán khách hàng của mình và vì thế giành được một vài triệu USD trong kinh doanh. Một vài ngàn các trường hợp đặc biệt một lần là những gì dẫn đến việc kinh doanh phức tạp "illogic" mà làm cho phần mềm kinh doanh rất khó khăn. Trong tình huống này, bạn phải tổ chức business logic hiệu quả như bạn có thể, bởi vì điều chắc chắn duy nhất là logic sẽ thay đổi theo thời gian. Đối với một số người, thuật ngữ "enterprise application" ngụ ý một hệ thống lớn. Tuy nhiên, điều quan trọng là phải nhớ rằng không phải tất cả các ứng dụng enterprise là rất lớn, mặc dù chúng có thể cung cấp rất nhiều giá trị cho enterprise. Nhiều người cho rằng, khi hệ thống nhỏ không lớn, chúng không đáng bận tâm. Nếu một hệ thống nhỏ thất bại, nó thường làm make noise ít hơn so với một hệ thống lớn. Nếu bạn có thể làm gì đó cải thiện các dự án nhỏ, thì là hiệu ứng tích lũy (cumulative effect) có thể rất đáng kể về một enterprise, đặc biệt là kể từ khi dự án nhỏ thường có giá trị không tương xứng. Thật vậy, một trong những điều tốt nhất bạn có thể làm là quay một dự án lớn thành một nhỏ (turn a large project into a small one) bởi đơn giản hóa kiến trúc và quá trình của nó (simplifying its architecture and process) .
  • 6. Các loại ứng dụng Enterprise Khi chúng ta thảo luận làm thế nào để thiết kế các ứng dụng enterprise (How to design enterprise application), và các patterns nào được sử dụng (What patterns to use), chúng ta nhận ra một điều rất quan trọng là các ứng dụng enterprise đều khác nhau và các vấn đề khác nhau đó dẫn đến nhiều cách khác nhau để đối ứng. Có rất nhiều các phương án khác nhau để lựa chọn, nhưng ở đây tôi sẽ chọn 3 points trên big plane rất lớn này. Hãy xem xét một B2C (business to customer) bán lẻ trực tuyến (online retailer): Người sử dụng duyệt web -- tạo giỏ mua hàng – mua hàng. Đối với một hệ thống như vậy, chúng ta cần có khả năng xử lý một số lượng rất lớn người sử dụng (very high volumn of users), vì vậy giải pháp của chúng ta cần đưa ra không chỉ hợp lý về hiệu quả sử dụng tài nguyên (reasonably efficient in terms of resources) mà còn có thể mở rộng (scalable) để bạn có thể làm tăng load (increase the load) bằng cách thêm phần cứng nhiều hơn (adding more hardware). Domain logic cho một ứng dụng như vậy có thể khá đơn giản: order capturing, some relatively simple pricing, shipping calculations, và shipment notification. Chúng ta muốn bất cứ ai đều có thể truy cập vào hệ thống một cách dễ dàng, do đó kéo theo (implies) một pretty generic Web presentation có thể được sử dụng với phạm vi rộng nhất có thể của các browsers. Data source bao gồm một cơ sở dữ liệu (database) để tổ chức (holding) các order và có lẽ một số communication với một hệ thống inventory để giúp cho information luôn available và delivery. Ngược lại với hệ thống trên là một hệ thống tự động hóa xử lý các thỏa thuận cho thuê (A system automates the processing of leasing agreements). Trong một vài cách thì hệ thống này đơn giản hơn nhiều so với B2C retailer vì có ít người dùng hơn nhiều, không quá 100 users tại một thời điểm. Nó phức tạp hơn là trong business logic. Tính toán các hóa đơn hàng tháng (Calculating monthly bills) trên một hợp đồng thuê, xử lý các event như early returns và late payments, và validating data như một lease được book là nhiệm vụ phức tạp, từ khi phần lớn các leasing industry's competition (đối thủ cạnh tranh) đi vào (come in) các dạng ít biến đổi hơn các giao dịch (deals) được thực hiện trong quá khứ . Một complex business domain như thế này là một thách thức vì các quy định là rất arbitrary (độc đoán). Một hệ thống như vậy cũng phức tạp hơn trong giao diện người dùng (UI). Ít nhất, điều này có nghĩa một giao diện HTML được gọi nhiều hơn với các screens phức tạp hơn (this means a much more involved HTML interface with more, and more complex, screens). Thường thì các hệ thống này có các UI demands (nhu cầu), cái mà dẫn các users tới mong muốn có một presentation phức tạp hơn so với một HTML front end cho phép, do đó, một giao diện rich-client thông thường (conventional) hơn là cần thiết. Một tương tác người dùng phức tạp hơn (more complex user interaction) cũng dẫn đến hành vi giao dịch (transaction behavior) phức tạp hơn: Booking a lease (đặt một hợp đồng thuê) có thể mất một hay hai giờ, trong thời gian đó người sử dụng trong một giao dịch hợp lý (logical transaction). Chúng ta cũng thấy một database schema phức tạp với khoảng hai trăm tables và các connections với các packages cho việc đánh giá tài sản (asset valuation) và định giá (pricing). Một điểm ví dụ thứ ba là một expense-tracking system cho một công ty nhỏ. Một hệ thống như vậy có vài users và logic đơn giản và có thể dễ dàng được truy cập trên toàn
  • 7. công ty với một HTML presentation. Data source chỉ một vài tables trong một database. Đơn giản như nó là, một hệ thống như thế này không phải là không có những thách thức. Bạn phải xây dựng nó rất nhanh chóng và bạn phải nhớ (bear in mind) rằng nó có thể phát triển như người ta muốn tính toán kiểm tra hoàn thuế (reimbursement checks), feed chúng vào hệ thống tính lương (payroll system), hiểu biết về thuế (understand tax implications), cung cấp các báo cáo cho các giám đốc tài chính (CFO), buộc (tie) vào các airline reservation Web services... Cố gắng sử dụng các architecture cho một trong hai system thí dụ khác sẽ làm chậm sự phát triển của hệ thống này. Nếu một hệ thống có lợi ích kinh doanh (như tất cả các ứng dụng enterprise nên), trì hoãn những lợi ích chi phí tiền bạc (delaying those benefits costs money). Tuy nhiên, bạn không muốn thực hiện các quyết định bây giờ mà sẽ cản trở sự tăng trưởng trong tương lai. Nhưng nếu bạn bổ sung thêm sự linh hoạt (flexibility) ngay từ bây giờ và mắc phải sai lầm (get it wrong), sự phức tạp được thêm vì tính linh hoạt của thực sự có thể làm cho nó khó khăn hơn để phát triển trong tương lai và có thể trì hoãn việc triển khai và do đó trì hoãn lợi ích. Mặc dù hệ thống như vậy có thể là nhỏ, hầu hết các enterprise có rất nhiều hệ thống như vậy vì thế hiệu quả tích lũy của một kiến trúc không phù hợp có thể là đáng kể (most enterprises have a lot of them so the cumulative effect of an inappropriate architecture can be significant.) Mỗi ứng dụng enterprise trong 3 thí dụ có các khó khăn, và chúng có các khó khăn khác nhau. Kết quả là bạn không thể chỉ với một architecture duy nhất mà sẽ giải quyết được cho cả ba. Chọn một architecture có nghĩa là bạn phải hiểu được những vấn đề cụ thể của hệ thống của bạn và chọn một thiết kế thích hợp dựa trên sự hiểu biết đó. Đó là lý do tại sao trong cuốn sách này, tôi không đưa ra một giải pháp duy nhất cho nhu cầu của doanh nghiệp của bạn. Thay vào đó, có nhiều pattern cho sự lựa chọn và thay thế. Ngay cả khi bạn chọn một pattern cụ thể, bạn sẽ phải thay đổi nó để đáp ứng nhu cầu của bạn. Bạn không thể xây dựng phần mềm enterprise mà không cần suy nghĩ, và tất cả bất kỳ cuốn sách có thể làm là cung cấp cho bạn thêm thông tin cho cơ sở quyết định của bạn trên. Nếu điều này áp dụng cho patterns, nó cũng áp dụng cho các tools. Mặc dù nó rõ ràng là cần sense (cảnh giác) để lựa chọn (pick) một tập các tool để phát triển các ứng dụng, bạn cũng phải nhận ra (recognize) các tools khác nhau là tốt nhất cho các mục đích khác nhau. Hãy coi chừng của việc sử dụng một tool thực sự là phù hợp với một loại ứng dụng khác nhau -- nó có thể cản trở nhiều hơn là giúp đỡ (it may hinder more than help). Suy nghĩ về hiệu năng – Performance Nhiều quyết định architecture là về hiệu suất (performance). Đối với hầu hết các vấn đề performance tôi thích có một system, đang chạy (running), đo đạc (instrument) nó, và sau đó sử dụng một quá trình tối ưu disciplined (có kỉ luật) dựa trên phép đo. Tuy nhiên, một số quyết định architecture ảnh hưởng đến hiệu suất theo một cách mà khó để fix tối ưu hóa sau này. Và ngay cả khi nó rất dễ để fix, người tham gia dự án phải lo lắng về những quyết định sớm. Luôn luôn khó khăn để nói về performance trong một cuốn sách như thế này. Lý do là nó quá khó để đưa ra bất cứ lời khuyên nào về performance, nó không nên được coi là thực tế (fact) cho đến khi nó được đo đạc trên cấu hình (configuration) của bạn. Tôi đã từng nhìn thấy các design được sử dụng hoặc bị reject vì cân nhắc hiệu suất, mà hóa ra không có một
  • 8. ai thực sự làm một số phép đo trên các setup thực tế được sử dụng cho các ứng dụng một lần. Tôi đưa ra một số guidelines trong cuốn sách này, bao gồm cả việc tổi thiểu các remote calls. Mặc dù vậy, bạn nên kiểm tra mỗi lời khuyên (tip) bằng cách đo trên ứng dụng của bạn. Tương tự như vậy có several occasions (một vài công việc) nơi mà code ví dụ trong cuốn sách này hy sinh (sacrifice) performance cho dễ hiểu (understandability). Một lần tùy thuộc vào quyết định của bạn (it's up to you) để áp dụng tối ưu hóa cho môi trường của bạn. Bất cứ khi nào bạn thực hiện tối ưu hóa hiệu suất, tuy nhiên, bạn phải đo cả trước và sau, nếu không, bạn chỉ có thể làm cho code của bạn khó đọc hơn. Có một hệ quả (corollary) quan trọng này: Một sự thay đổi đáng kể trong configuration có thể vô hiệu bất kỳ sự thật (invalidate any facts) về hiệu suất. Vì vậy, nếu bạn nâng cấp lên một phiên bản mới của virtual machine, hardware, database, hoặc bất cứ điều gì khác, bạn phải redo tối ưu hóa hiệu suất của bạn và đảm bảo rằng chúng vẫn đang được giúp đỡ. Trong nhiều trường hợp một configuration mới có thể thay đổi mọi thứ. Thật vậy, bạn có thể thấy rằng một tối ưu hóa mà bạn đã làm trong quá khứ để cải thiện performance thực sự làm hư (hurts) performance trong môi trường mới. Một vấn đề khác khi nói về hiệu suất là thực tế có nhiều thuật ngữ được sử dụng một cách inconsistent (không tương xứng/không nhất quán). Nạn nhân (victim) được chú ý nhiều nhất của việc này là thuật ngữ "khả năng mở rộng (scalability)", nó thường xuyên được sử dụng có nghĩa như half of dozen (nửa tá) những cái khác nhau. Dưới đây là các thuật ngữ tôi sử dụng. Thời gian đáp ứng (response time) là lượng thời gian (amount of time) cần cho hệ thống (it takes for the system) xử lý một request từ bên ngoài. Đây có thể là UI action, chẳng hạn như nhấn một nút, hoặc call một Server API. Đáp ứng (responsiveness) là về một hệ thống công nhận (acknowledges) một request nhanh như thế nào (how quickly the system acknowledges a request) trái ngược với việc xử lí nó (as opposed to processing it). Điều này rất quan trọng trong nhiều hệ thống bởi vì người dùng có thể thất vọng nếu một hệ thống có responsiveness thấp, thậm chí nếu response time của nó là tốt. Nếu hệ thống của bạn chờ đợi (waiting) trong suốt toàn bộ yêu cầu (whole request), thì responsiveness và response time là như nhau. Tuy nhiên, nếu bạn tỏ ra (indicate) rằng bạn đã nhận được request trước khi bạn hoàn thành, thì responsiveness của bạn là tốt hơn. Cung cấp một progress bar trong suốt quá trình copy file cải thiện responsiveness của UI của bạn, mặc dù nó không cải thiện response time. Độ trễ (latency) là thời gian tối thiểu cần thiết để có được bất kỳ form của response, thậm chí nếu công việc phải được thực hiện là không tồn tại. Đó thường là những vấn đề lớn trong các hệ thống từ xa (remote systems). Nếu tôi yêu cầu (ask) một chương trình không làm gì cả, nhưng để cho tôi biết (to tell me) khi nó đã không không làm gì cả, thì tôi nên nhận được response gần như tức thời (almost instantaneous ) nếu chương trình chạy trên laptop của tôi. Tuy nhiên, nếu chương trình chạy trên một máy tính từ xa (remote computer), tôi có thể mất một vài giây chỉ vì thời gian thực hiện đối với các request và response để thực hiện theo cách của chúng trên đường truyền (wire). Là một application developer, tôi thường không thể làm gì để cải thiện độ trễ (latency). Độ trễ cũng là lý do tại sao bạn nên giảm thiểu các cuộc gọi từ xa (remote calls).
  • 9. Thông lượng (throughput) là bao nhiêu stuff mà bạn có thể làm trong một khoảng thời gian nhất định (amount of time). Nếu bạn đang tính giờ (timing) thời gian sao chép của một tập tin, thông lượng có thể được đo bằng bytes/s. Đối với các ứng dụng enterprise là phép đo thông thường (typical measure) là số transactions/s (tps). Nhưng vẫn đề là nó phục thuộc vào độ phức tạp các transaction của bạn. Đối với hệ thống cụ thể của bạn, bạn nên chọn một tập các transaction phổ biến. Trong thuật ngữ performance này là một trong hai throughput hoặc response time – cái nào quan trọng hơn đối với bạn. Đôi khi có thể khó khăn để nói về performance khi một kỹ thuật cải thiện throughput nhưng giảm response time, vì vậy tốt nhất để sử dụng các thuật ngữ chính xác hơn. Từ góc độ người dùng responsiveness có thể quan trọng hơn response time, vì vậy nâng cao responsiveness phải trả bằng (at the cost of) response time hoặc throughput sẽ làm tăng hiệu suất. Tải (load) là một phát biểu (statement) có 'how much stress a system is under', mà có thể đo được bằng có bao nhiêu users đang kết nối với nó (how many users are currently connected to it). Load thường là một context cho một phép đo khác, chẳng hạn như response time. Như vậy, bạn có thể nói rằng response time đối với một số request là 0,5s với 10 users và 2s với 20 users. Độ nhạy tải (load sensitivity) là một biểu hiện (expression) của response time thay đổi như thế nào với load (how the response time varies with the load). Hãy nói rằng hệ thống A có response time là 0,5s cho 10 users đến 20 users và hệ thống B có response time là 0,2s cho 10 users và tăng lên đến 2s cho 20 users. Trong trường hợp này hệ thống A có load sensitivity thấp hơn so với hệ thống B. Chúng tôi cũng có thể sử dụng sự suy thoái (degradation) để nói rằng hệ thống B làm giảm hơn so với hệ thống A. Hiệu quả (efficiency) là hiệu suất được phân chia bởi các resources. Một hệ thống mà được 30 TPS trên 2 CPUs có hiệu quả hơn một hệ thống mà được 40 TPS trên 4 CPUs giống hệt nhau. Khả năng của một hệ thống (capacity of a system) là một sử biểu thị (indication) của effective throughput lớn nhất hoặc load. Đây có thể là một lớn nhất tuyệt đối (absolute maximum) hay một điểm (point) mà tại đó performance lặn (dips) dưới một ngưỡng (threshold) chấp nhận được. Khả năng mở rộng (scalability) là một thước đo (measure) của thêm resource (thường là hardware) ảnh hưởng đến performance như thế nào (how adding resouces affects performance). Một hệ thống scalable cho phép bạn thêm hardware và đạt được cải thiện về performance tương xứng, chẳng hạn như tăng gấp đôi số servers, bạn có tăng gấp đôi throughput của bạn. Khả năng mở rộng theo chiều dọc (vertical scalability), hoặc scaling up, có nghĩa là thêm nhiều power cho một server duy nhất, chẳng hạn như nhiều memory hơn. Khả năng mở rộng theo chiều ngang (horizontal scalability), hay scaling out, có nghĩa là thêm nhiều servers. Vấn đề ở đây là các quyết định thiết kế (design decisions) không ảnh hưởng đến tất cả các yếu tố performance như nhau (equally). Nói rằng chúng ta có hai hệ thống phần mềm chạy trên một server: Capacity của Swordfish là 20 TPS trong khi Capacity của Camel là 40 TPS. Cái nào có performance tốt hơn? Cái nào có scalable hơn? Chúng ta không thể trả
  • 10. lời câu hỏi scalable từ dữ liệu này, và chúng ta chỉ có thể nói rằng Camel là hiệu quả hơn trên một server duy nhất. Nếu chúng ta thêm một server khác, chúng ta nhận thấy rằng Swordfish bây giờ xử lý 35 TPS và Camel xử lý 50 TPS. Capacity của Camel là vẫn tốt hơn, nhưng Swordfish có vẻ như nó có thể scale out tốt hơn. Nếu chúng ta tiếp tục bổ sung thêm các servers chúng ta sẽ nhận ra rằng Swordfish được 15 TPS trên mỗi server thêm và Camel được 10. Với những dữ liệu này, chúng ta có thể nói rằng Swordfish scale out tốt hơn, mặc dù Camel là hiệu quả hơn khi có ít hơn 5 servers. Khi xây dựng các hệ thống enterprise, nó thường make sense để xây dựng cho hardware scalability hơn là capacity hoặc thậm chí efficiency. Scalability cung cấp cho bạn tùy chọn performance tốt hơn nếu bạn cần nó. Scalability cũng có thể được dễ dàng hơn để làm. Thường thì các designer làm những điều phức tạp để cải thiện capacity trên một nền tảng phần cứng cụ thể (particular hardware platform) khi nó thực sự có thể rẻ hơn để nhiều hardware hơn. Nếu Camel có một chi phí lớn hơn Swordfish, và chi phí lớn hơn tương đương với một couple servers, thì Swordfish kết thúc (ends up) là rẻ hơn thậm chí nếu bạn chỉ cần 40 TPS. Nó là fashionable để phàn nàn về việc phải dựa vào hardware tốt hơn để làm cho software của chúng ta chạy đúng, và tôi tham gia ca đoàn này bất cứ khi nào tôi phải nâng cấp máy tính xách tay của tôi chỉ để xử lý các phiên bản mới nhất của Word. Nhưng hardware mới hơn thường rẻ hơn so với làm cho phần mềm chạy trên các hệ thống chưa mạnh mẽ. Tương tự như vậy, thêm nhiều servers thường rẻ hơn so với việc thêm các programmers -- cung cấp một hệ thống scalable. Các khuôn mẫu – Patterns Không có định nghĩa một pattern chung nào được chấp nhận, nhưng có lẽ là nơi tốt nhất (best place) để bắt đầu là Christopher Alexander, một nguồn cảm hứng cho nhiều người đam mê pattern: "Mỗi pattern mô tả một vấn đề xảy ra lặp đi lặp lại (over and over again) trong environment của chúng ta, và sau đó mô tả core của solution cho vấn đề đó, trong một cách (in such a way) mà bạn có thể sử dụng solution này trên một triệu lần, mà không bao giờ làm việc đó cùng một cách hai lần (doing it the same way twice)"[Alexander et al.]. Alexander là một kiến architect, vì vậy ông nói về các building, nhưng định nghĩa này làm việc khá tốt đối với software. Trọng tâm của pattern (The focus of the pattern) là một giải pháp cụ thể, một giải pháp common và hiệu quả trong việc giải quyết một hoặc nhiều vấn đề tái diễn (recurring). Một cách nhìn khác là một pattern là một chunk of advice và nghệ thuật tạo mẫu (the art of creating patterns) là divide up many pieces of advice into relatively independent chunks để bạn có thể refer tới chúng và discuss chúng nhiều hoặc ít hơn một cách riêng biệt. Một key part của pattern là chúng bắt nguồn từ thực tế. Bạn tìm thấy các patterns bằng cách nhìn vào những gì người ta làm (looking at what people do), quan sát những gì làm việc (observing things that work), và sau đó tìm kiếm các "core of the solution“ Nó không phải là một quá trình dễ dàng, nhưng một khi bạn đã tìm thấy một số pattern tốt chúng trở thành một cái gì đó có giá trị (valuable thing). Đối với tôi giá trị của chúng nằm ở chỗ có thể tạo ra một cuốn sách như là một tài liệu tham khảo. Bạn không cần phải đọc tất cả các cuốn sách này, hoặc tất cả của bất kỳ cuốn sách pattern khác, để tìm thấy nó hữu ích. Bạn chỉ cần phải đọc đủ (read enough) để ý thức (sense) các patterns là gì (what the patterns are), chúng giải quyết các vấn đề gì (what problems they solve), và chúng giải quyết vấn đề như thế nào (how they solve them). Bạn không cần phải biết tất cả các chi
  • 11. tiết nhưng phải đủ để nếu bạn đi vào một trong những vấn đề thì bạn có thể tìm thấy các pattern trong cuốn sách. Chỉ khi đó bạn cần hiểu thực sự sâu (in depth) về pattern. Khi bạn cần pattern, bạn phải tìm hiểu làm thế nào để áp dụng (how to apply) nó vào hoàn cảnh của bạn. Một điều quan trọng (key thing) về patterns là bạn không bao giờ áp dụng các giải pháp một cách mù quáng, đó là lý do tại sao các pattern tools đã thất bại thê thảm như vậy. Tôi muốn nói rằng các patterns là "half baked," điều đó có nghĩa là bạn luôn luôn phải hoàn thành chúng trong lò nướng (oven) dự án của riêng bạn. Mỗi lần tôi sử dụng một pattern tôi tinh chỉnh nó một chút ở chỗ này và một chút chỗ khác. Bạn thấy là 'same solution many times over, but it's never exactly the same..' Mỗi pattern là tương đối độc lập, nhưng các patterns không bị cô lập (isolated) với các pattern khác. Thường thì một pattern dẫn đến một pattern khác hoặc một pattern xảy ra (occurs) chỉ xung quanh pattern khác. Như vậy, bạn thường chỉ nhìn thấy Class Table Inheritance nếu có một Domain Model trong thiết kế của bạn. Ranh giới giữa các pattern là mờ (naturally fuzzy), nhưng tôi đã cố gắng để làm cho mỗi pattern như self-standing như tôi có thể. Nếu ai đó nói "Sử dụng một Unit of Work," bạn có thể nhìn tìm nó và xem làm thế nào để áp dụng nó mà không cần phải đọc toàn bộ cuốn sách. Nếu bạn là một nhà thiết kế giàu kinh nghiệm cho các ứng dụng enterprise, bạn có thể sẽ thấy rằng hầu hết các patterns đều quen thuộc với bạn. Tôi hy vọng bạn sẽ không quá thất vọng. Các patterns không phải các ý tưởng gốc (original ideas); chúng tôi quan sát nhiều những gì xảy ra trong lĩnh vực này. Kết quả là, chúng tôi không nói rằng chúng tôi "phát minh (invented)" một pattern mà thay vào đó, chúng tôi đã "phát hiện (discovered)" ra một pattern. Vai trò của chúng tôi là ghi chú (note) các giải pháp chung (common solution), tìm kiếm cốt lõi của nó (its core), và sau đó viết ra các resulting pattern. Đối với một nhà thiết kế giàu kinh nghiệm, giá trị của pattern không phải là nó cung cấp cho bạn một ý tưởng mới; các giá trị nằm trong việc giúp bạn giao tiếp ý tưởng của bạn (the value lies in helping you communicate your idea). Nếu bạn và đồng nghiệp của bạn đều biết Remote Facade là gì, bạn có thể giao tiếp rất nhiều bằng cách nói, "Class này là một Remote Facade." Nó cũng cho phép bạn nói với người mới, "Sử dụng một đối Data Transfer Object cho điều này," và họ có thể đến với cuốn sách này để tìm nó. Kết quả là các patterns đó tạo ra một vocabulary về thiết kế, đó là lý do tại sao đặt tên (naming) là một vấn đề quan trọng như vậy. Trong khi hầu hết các patterns thực sự truly cho ứng dụng enterprise, các patterns trong chương base patterns là tổng quát hơn và localized (cục bộ). Tôi bao gồm họ, vì tôi tham khảo chúng trong các cuộc thảo luận của các mô hình ứng dụng doanh nghiệp. Cấu trúc của các khuôn mẫu – The Structure of the Patterns Mỗi tác giả có lựa chọn pattern form của hoj. Một số form dựa trên một cuốn sách classic patterns như [Alexander et al.], [Gang of Four], hoặc [POSA]. Những người khác làm theo ý của họ. Tôi đã vật lộn với việc điều gì làm cho form là tốt nhất (what makes the best form). Một mặt tôi không muốn một cái gì đó nhỏ như là GOF form; Mặt khác tôi cần phải có section hỗ trợ một cuốn sách tham khảo. Vì vậy, đây là những gì tôi đã sử dụng cuốn sách này.
  • 12. Item đầu tiên là tên của Pattern. Tên Pattern là rất quan trọng, bởi vì một phần của mục đích của các patterns là để tạo ra một vocabulary cho phép các designer giao tiếp hiệu quả hơn. Vì vậy, nếu tôi nói cho bạn biết Web server của tôi được xây dựng xung quanh một Front Controller và một Transform View và bạn biết các mô hình, bạn có một ý tưởng rất rõ ràng về kiến trúc máy web server của tôi. Tiếp theo là hai item đi với nhau: mục đích (intent) và bản phác thảo (sketch). Intent tổng kết các patterns trong một hoặc hai câu; Sketch là một biểu diễn trực quan (visual representation) của pattern, thường xuyên, nhưng không phải luôn luôn là một UML diagram. Ý tưởng là để tạo ra một reminder ngắn gọn về what the pattern is about , do đó bạn có thể nhanh chóng gọi nó. Nếu bạn thật sự "have the pattern," có nghĩa là bạn biết các giải pháp ngay cả khi bạn không biết tên, thì intent và sketch nên là tất cả bạn cần phải biết pattern là gì (what the pattern is). Phần tiếp theo mô tả một motivating problem cho các pattern. Điều này có thể không phải là vấn đề duy nhất mà các pattern giải quyết, nhưng nó là một cái mà tôi nghĩ là động lực tối nhất cho pattern. 'How It Works' mô tả các giải pháp. Ở đây tôi đặt một cuộc discussion về vấn đề thực hiện và variations (biến thể) tôi đã trải qua. Các discussion là độc lập nhất có thể với bất kỳ platform cụ thể nào. 'When to Use It' mô tả khi nào các pattern nên được sử dụng. Ở đây tôi nói về trade-offs cái mà làm cho bạn lựa chọn giải pháp này so với các giải pháp khác. Nhiều patterns trong cuốn sách này là các lựa chọn thay thế: Page Controller và Front Controller. Vài pattern luôn là lựa chọn đúng, vì vậy bất cứ khi nào tôi tìm thấy một pattern tôi luôn tự hỏi, "Khi nào tôi không nên sử dụng điều này?" Câu hỏi đó thường dẫn tôi đến pattern thay thế. Phần Further Reading chỉ bạn tới các thảo luận khác của pattern này. Đây không phải là một tài liệu tham khảo toàn diện. Tôi đã giới hạn tài liệu tham khảo của tôi tới các pieces mà tôi nghĩ là quan trọng trong việc giúp bạn hiểu các patterns, vì vậy tôi đã loại bỏ bất kỳ thảo luận mà tôi không nghĩ bổ sung thêm nhiều cái với những gì tôi đã viết và tất nhiên tôi đã loại bỏ các cuộc thảo luận của các pattern mà tôi chưa đọc. Tôi cũng đã không đề cập mục mà tôi nghĩ rằng sẽ thật khó tìm được, hoặc liên kết Web không ổn định mà tôi sợ có thể biến mất theo thời gian bạn đọc cuốn sách này. Tôi muốn thêm một số ví dụ. Mỗi ví dụ là một ví dụ đơn giản của pattern sử dụng (in use), minh họa bằng một số mã trong Java hoặc C #. Tôi đã chọn những ngôn ngữ đó bởi vì chúng có vẻ là các ngôn ngữ mà số lượng lớn nhất của các lập trình viên chuyên nghiệp có thể đọc. Các ví dụ này không phải là pattern. Khi bạn sử dụng các pattern, nó sẽ không giống chính xác như ví dụ này nên không đối xử với nó như một loại glorified (vinh hiển / ca ngợi) macro. Tôi đã cố tình giữ ví dụ như đơn giản càng tốt, do đó bạn có thể xem các pattern trong một form rõ ràng như tôi có thể tưởng tượng. Tất cả các loại vấn đề bị bỏ qua mà sẽ trở nên quan trọng khi bạn sử dụng nó, nhưng đó sẽ là vấn đề cụ thể với môi trường của riêng bạn. Đây là lý do tại sao bạn luôn phải chỉnh pattern. Một trong những hậu quả của việc này là tôi đã làm việc chăm chỉ để giữ cho mỗi ví dụ đơn giản như tôi có thể, trong khi vẫn còn minh họa thông điệp cốt lõi của nó. Vì vậy, tôi thường chọn một ví dụ đơn giản và rõ ràng, chứ không phải là một trong những minh chứng như thế
  • 13. nào một mô hình hoạt động với nhiều wrinkles (nếp nhăn) yêu cầu trong một hệ thống sản xuất. Đó là một sự cân bằng khéo léo (tricky balance) giữa sự đơn giản (simple) và đơn giản (simplistic), nhưng thật sự có quá nhiều vấn đề peripheral (ngoại biên) thực tế có thể làm cho khó khăn hơn để hiểu những điểm chính (key point) của một pattern. Đây cũng là lý do tại sao tôi cho các ví dụ đơn giản độc lập (simple indenpendent examples) thay vì một ví dụ chạy kết nối (connected running examples). Các ví dụ độc lập (Independent examples) là dễ hơn để hiểu độc lập (in isolation), nhưng đưa ra ít chỉ dẫn về cách bạn đặt chúng lại với nhau như thế nào. Một ví dụ kết nối (connected example) chỉ ra như thế nào chúng phù hợp với nhau (how things fit together), nhưng thật khó để hiểu được bất kỳ một pattern mà không hiểu tất cả những cái khác có liên quan trong ví dụ. Trong khi về mặt lý thuyết nó có thể tạo ra các ví dụ được kết nối mà có thể hiểu một cách độc lập, làm như vậy là rất khó hoặc ít nhất là quá khó đối với tôi-nên tôi đã chọn con đường độc lập. Code trong các ví dụ được viết với việc tập trung vào việc làm cho các ý tưởng dễ hiểu. Kết quả, several things fall aside -- , đặc biệt, erro handling (xử lý lỗi), mà tôi không quan tâm nhiều đến vì tôi đã không phát triển bất cứ pattern ở khu vực này. Chúng hoàn toàn là để minh họa mô hình. Chúng không có ý định để chỉ ra như thế nào để mô hình bất kỳ vấn đề kinh doanh cụ thể. Không phải tất cả các sections xuất hiện trong tất cả các mẫu. Nếu tôi không thể nghĩ ra một ví dụ điển hình hoặc motivation text, tôi bỏ nó ra (I left it out). Giới hạn của các khuôn mẫu – Limitations of These Patterns Cuốn sách này phản ánh sự hiểu biết hiện tại của tôi, và sự hiểu biết đó đã phát triển như tôi đã viết cuốn sách. Tôi hy vọng nó sẽ tiếp tục phát triển lâu sau khi cuốn sách đã xuất bản. Một điều chắc chắn của sự phát triển phần mềm là nó không bao giờ đứng yên. Khi bạn xem xét sử dụng các pattern, không bao giờ quên rằng chúng là một điểm khởi đầu, không phải là một điểm đến cuối cùng. Không có cách nào mà bất kỳ tác giả có thể xem tất cả các biến thể mà nhiều dự án phần mềm có. Tôi đã viết những pattern để giúp cung cấp một khởi đầu, vì vậy bạn có thể đọc về những bài học mà tôi và những người tôi đã quan sát, đã học được từ làm và gặp khó khăn. Luôn luôn nhớ rằng mỗi pattern là không đầy đủ và rằng bạn có trách nhiệm, và sự vui vẻ, hoàn thành nó trong bối cảnh hệ thống của riêng bạn.
  • 14. 2 Chapter 1. Layering Layering là một trong những kỹ thuật phổ biến nhất mà các software designer sử dụng để phá vỡ (break apart) một hệ thống phần mềm phức tạp (complicated software system). Bạn nhìn thấy nó trong các kiến trúc máy tính (machine architectures), nơi các layers kế thừa (descend) từ một ngôn ngữ lập trình với OS calls vào device drivers và tập lệnh (instruction sét) CPU, và vào cổng logic (logic gates) bên trong chips. Networding có FTP layered phía trên của TCP (on top of TCP), tầng mà ở phía trên IP (on top of IP), tầng này phía trên của Ethernet (on top of Ethernet). Khi suy nghĩ về một system trong terms of layers, bạn hãy tưởng tượng các principal subsytems (hệ thống con chính) trong software được sắp xếp trong một số form của layer cake, trong đó mỗi layer dựa trên một layer thấp hơn. Trong schema này, các layer cao hơn sử dụng các service khác nhau được định nghĩa (define) bởi các layer thấp hơn, nhưng các layer thấp hơn là không biết gì về các layer cao hơn. Hơn nữa, mỗi layer thường giấu (hide) các layer thấp hơn của nó từ các layer trên, như vậy layer 4 sử dụng các service của layer 3, layer mà sử dụng các service của layer 2, layer 4 không biết gì về layer 2. (Không phải tất cả các kiến trúc layering được mờ đục (opaque) như thế này , nhưng hầu hết là-hay đúng hơn là hầu hết chủ yếu là opaque. Phân tách (breaking down) một hệ thống thành các layers có một số lợi ích quan trọng:  Bạn có thể hiểu một single layer như là một coherent whole mà không cần biết nhiều về các layer khác. Bạn có thể hiểu làm thế nào để xây dựng một service FTP phía trên TCP (on top of TCP) mà không biết các chi tiết xem ethernet làm việc như thế nào.  Bạn có thể thay thế (substitute) các layer với việc thực hiện (implementation) thay đổi nhau (alternative) của các services basic giống nhau. Một service FTP có thể chạy mà không có sự thay đổi qua ethernet, PPP, hoặc bất cứ một cable company sử dụng.  Bạn giảm thiểu sự phụ thuộc giữa các layers. Nếu các cable company thay đổi hệ thống truyền dẫn vật lý của nó, chúng ta không cần phải thay đổi service FTP của chúng ta.  Các layers là nơi tốt cho chuẩn hóa (standardization). TCP và IP là tiêu chuẩn bởi vì chúng định nghĩa các layers nên hoạt động như thế nào.  Một khi bạn đã xây dựng một layer, bạn có thể sử dụng nó cho nhiều services mức cao hơn (higher-level). Như vậy, TCP / IP được sử dụng bởi FTP, telnet, SSH, và HTTP. Nếu không, tất cả các giao thức mức cao hơn (higher-level) sẽ phải viết các giao thức mức thấp hơn (lower-level) cho chính các giao thức đó.
  • 15. Layering là một kỹ thuật quan trọng, nhưng có nhược điểm (downsides):  Các Layer gói một số 'things', nhưng không phải tất cả. Kết quả là đôi khi bạn nhận được tầng (cascading) thay đổi. Các ví dụ điển hình của việc này trong một ứng dụng enterprise được phân tầng (layered) được bổ sung thêm một field mà cần phải hiển thị trên UI, phải có trong cơ sở dữ liệu, và do đó phải được thêm vào mọi layer ở giữa.  Các extra layers (tầng đệm) có thể gây tổn hại cho performance. Tại mỗi layers 'things' thường cần phải được chuyển đổi (transformed) từ một layer này tới layer khác. Tuy nhiên, việc đóng gói của một chức năng underlying thường mang đến cho bạn hiệu quả đạt được hơn là bù đắp. Một layer kiểm soát các transaction có thể được tối ưu hóa và sau đó sẽ làm cho mọi thứ nhanh hơn. Nhưng phần khó nhất của một kiến trúc phân tầng (layered) là quyết định xem các layers có cái gì và trách nhiệm của mỗi layer nên là gì (what layers to have and what the responsibility of each layer should be). Sự phát triển (Evolution) của các Layers trong các ứng dụng Enterprise Trong những ngày đầu (early days) của batch systems, tôi không cảm nhận được rằng người ta nghĩ nhiều về layers trong những ngày đó. Bạn viết một chương trình để thao tác một số form của các files (ISAM, VSAM, vv), và đó là ứng dụng của bạn. Không có layers cần áp dụng. Các khái niệm về layers trở nên rõ ràng hơn trong những năm 90s với sự nổi lên của các client-server systems. Đây là hệ thống 2-layers: Các client chứa (held) UI và code ứng dụng khác, và các servers thường là một relation database. Các tools client phổ biển là VB, PowerBuilder, và Delphi. Các tools này làm cho việc xây dựng các ứng dụng data-intensive trở nên dễ dàng, giống như chúng hỗ trợ UI Widget đã nhận thức (aware) SQL. Vì vậy bạn có thể xây dựng một screen bằng cách kéo các control vào design area và sau đó sử dụng các property sheet để kết nối các controls tới database. Nếu ứng dụng chỉ là hiển thị và cập nhật dữ liệu relational đơn giản, thì các hệ thống client- server làm việc rất tốt. Vấn đề đến với domain logic: các business rules, các validations, các calculations, ... Thông thường người ta sẽ viết chúng trên client, nhưng điều này là vụng về (awkward) và thường được thực hiện bằng cách nhúng logic trực tiếp vào UI screen. Khi domain logic trở nên phức tạp hơn, code này trở nên rất khó khăn để làm việc. Hơn nữa, nhúng logic vào screen làm cho nó dễ duplicate code, điều này có nghĩa rằng những thay đổi đơn giản dẫn đến hunting down các similar code trong nhiều screens. Một thay thế là đặt domain logic trong database giống như stored procedures. Tuy nhiên, các store procedures ‘gave limited structuring mechanisms’, trong đó một lần nữa dẫn đến awkard code. Ngoài ra, nhiều người thích relational database bởi vì SQL là một chuẩn đã cho phép họ thay đổi database vendor của họ. Mặc dù thực tế rằng rất ít người thực sự đã làm
  • 16. điều này, nhiều người thích có các option để thay đổi vendor mà không mất một chi phí cho porting quá cao. Bởi vì chúng là độc quyền (all proprietary), stored procedures đã loại bỏ option đó. Tại cùng thời điểm mà client-server phổ biến, thế giới hướng đối tượng (object-oriented) đã nổi lên (rising). Cộng đồng ‘object’ có một câu trả lời cho vấn đề domain miền: Di chuyển tới một hệ thống 3-layer. Trong phương pháp này, bạn có một presentation layer cho UI của bạn, một domain layer cho domain logic của bạn, và một data source. Bằng cách này bạn có thể di chuyển tất cả domain logic miền ra khỏi UI và đặt nó vào một layer mà bạn có thể cấu trúc nó đúng với các objects. Mặc dù vậy, đối tượng bandwagon thực hiện (made) chút tiến triển. Sự thật là nhiều hệ thống là đơn giản, hoặc ít nhất là bắt đầu theo cách đó. Và mặc dù các phương pháp 3-layer có nhiều lợi ích, các tools cho client-server là hấp dẫn (compelling) nếu vấn đề của bạn là đơn giản. Các tools client-server cũng gặp nhiều khó khăn, hoặc thậm chí không thể, để sử dụng trong một cấu hình 3-layer. Tôi nghĩ rằng các seismic shock (cú sốc lớn/địa chấn) ở đây là sự nổi lên của Web (the rise of the Web). Đột nhiên người ta muốn triển khai các ứng dụng client-server với Web browser. Tuy nhiên, nếu tất cả các business logic của bạn đã được buried (chôn) trong một rich client, thì tất cả các business logic của bạn cần phải được làm lại (redone) để có Web interface. Một hệ thống 3-layer được thiết kế tốt chỉ có thể thêm một presentation layer mới và được thực hiện với nó. Hơn nữa, với Java, chúng ta thấy một ngôn ngữ hướng đối tượng unashamedly nhấn chủ đạo (hit the mainstream). Các tools xuất hiện để xây dựng các Web pages là ít hơn (much less) gắn với SQL và vì vậy nhiều trách nhiệm (amenable) tới 3rd layer. Khi mọi người thảo luận layering, thường có một số nhầm lẫn giữa 2 thuật ngữ: layer và tier. Thường thì cả hai được sử dụng như là từ đồng nghĩa, nhưng hầu hết mọi người nhìn thấy tier ngụ ý phân tách mức vật lí (physical separation). Hệ thống client-server thường được mô tả như là hệ thống 2-tier, và sự phân tách là physical: Các client là một desktop và server là một server. Tôi sử dụng layer để nhấn mạnh rằng bạn không cần phải chạy các layers trên các máy khác nhau. Một layer riêng biệt của domain logic thường chạy trên một desktop hoặc database server. Trong tình huống này, bạn có 2 nodes nhưng 3 layers riêng biệt. Với một loacl database tôi có thể chạy cả 3 layers trên một laptop duy nhất, nhưng vẫn sẽ có 3 layers riêng biệt. Ba Layers chính (The Three Principal Layers) Đối với cuốn sách này, tôi đang tập trung thảo luận về kiến trúc của 3 layers chính: presentation, domain, và data source. Bảng 1.1 tóm tắt các layers. Presentation logic là về như thế nào để xử lý sự tương tác (how to handle) giữa user và software. Điều này có thể đơn giản như một command-line hoặc text-based menu system, nhưng ngày nay nó thường là một rich-client graphics UI hoặc một HTML-
  • 17. based browser UI. Trách nhiệm (responsibilities) chính của các presentation layer là để hiển thị thông tin tới user và thông dịch (interpret) các commands từ user vào các action theo domain và data source. Layer Responsibilities Presentation Cung cấp các services, hiển thị thông tin (ví dụ, trong Windows hoặc HTML), xử lí các user request (nhấp chuột, gõ keyboard), HTT[ requests, lời gọi command-line, batch API Domain Logic đó là real point của hệ thống Data Source Giao tiếp với databases, messaging systems, transaction managers, và các gói khác Data source logic là về giao tiếp với các systems khác để thực hiện task đại diện (behalf of) cho các ứng dụng. Đây có thể là transaction monitors, các ứng dụng khác, messaging systems, ... Đối với hầu hết các ứng dụng enterprise phần lớn nhất của data source logic là một database mà trách nhiệm chính của nó là lưu trữ persistent data. Phần còn lại là domain logic, cũng được gọi là business logic. Đây là công việc (work) mà ứng dụng cần phải làm cho các domain mà bạn đang làm việc. Nó liên quan đến việc tính toán (calculations) dựa trên các inputs và stored data, kiểm tra (validation) dữ liệu (any data) đến từ presentation, và tính toán (figuring out) chính xác những gì data source logic để dispatch (what data source logic to dispatch), tùy thuộc vào các commands nhận được từ presentation. Đôi khi các layers được sắp xếp để domain layer hoàn toàn ẩn các data source từ presentation. Thường xuyên hơn (more often), tuy nhiên, presentation truy xuất data store trực tiếp. Trong khi điều này là ít pure (tinh khiết), nó có xu hướng làm việc tốt hơn trong thực tế. Presentation có thể thông dịch (interpret) một command từ user, sử dụng data source để kéo (pull) dữ liệu có liên quan ra khỏi database, và sau đó để cho domain logic thao tác (manipulate) data đó trước khi trình bày (presenting) nó trên glass. Một ứng dụng đơn lẻ (single) thường có thể có nhiều packages của mỗi một trong 3 subject Một ứng dụng được thiết kế để được thao tác không chỉ bởi các end users thông qua một rich-client interface mà còn thông qua một command line sẽ có 2 presentations: một cho rich-client interface và một cho command line. Nhiều thành phần (component) data source có thể biểu diễn (present) cho các database khác nhau, nhưng sẽ là đặc biệt (particularly) để giao tiếp với các packages đang tồn tại. Ngay cả domain có thể được chia (broken into) thành các khu vực riêng biệt (distinct areas) tương đối tách biệt với nhau. Một số data source packages nhất định chỉ có thể được sử dụng bởi các domain packages nhất định.
  • 18. Cho đến nay (So far) tôi đã nói về một user. Điều này đưa ra (raise) câu hỏi về điều gì xảy ra (what happen) khi không có là một human nào điều khiển (driving) các software. Đây có thể là một cái gì đó mới và fashionable như một Web service hoặc một cái gì đó nhàm chán và hữu ích như một batch process. Trong trường hợp sau, user là các client program. Tại điểm này (at this point), nó trở nên rõ ràng rằng có rất nhiều điểm giống nhau giữa các presentation và data source layers trong đó cả hai đều là về kết nối với thế giới bên ngoài (outside world). Đây là logic đằng sau (behind) Hexagonal Architecture pattern của Alistair Cockburn, mà hình dung (visualizes) bất kỳ hệ thống nào giống như là một core được bao quanh (surrounded) bởi các interfaces với các hệ thống bên ngoài (external systems). Trong Hexagonal Architecture tất cả mọi thứ về cơ bản là outside interface, và do đó là một quan điểm đối xứng (symmetrical view) chứ không phải là layering schema không đối xứng của tôi. Tôi tìm thấy bất đối xứng này hữu ích (useful), tuy nhiên, bởi vì tôi nghĩ rằng có một sự phân tách tốt (good distinction) được thực hiện giữa một interface mà bạn cung cấp như một service cho others và sử dụng service của người khác. Đi sâu vào phần core, đây là sự khác biệt thực sự tôi làm giữa các presentation và data source. Presentation là một external interface cho một service mà hệ thống của bạn cung cấp cho người khác, cho dù đó là complex human hoặc một remote program đơn giản. Data source là interface cho những cái (things) đang cung cấp một service cho bạn. Tôi tìm ra nó có lợi để suy nghĩ về chúng khác nhau vì sự khác biệt trong các client làm thay đổi cách bạn suy nghĩ về service. Mặc dù chúng ta có thể xác định được 3 responsibility layers chung của pressentation, domain, và data source cho mỗi ứng dụng enterprise, làm thế nào bạn phân tách (separation) chúng phụ thuộc vào độ phức tạp của ứng dụng như thế nào. Một script đơn giản để pull data từ database và hiển thị nó trong một Web page tất cả có thể là một procedure. Tôi vẫn sẽ nỗ lực để tách 3 layers, nhưng trong trường hợp đó tôi có thể làm điều đó chỉ bằng cách đặt các behavior của mỗi layer trong subroutines riêng biệt. Khi hệ thống trở nên phức tạp hơn, tôi sẽ phá vỡ (break) 3 layers thành các classes riêng biệt. Nếu như hệ thống phức tạp tăng lên, tôi sẽ chia layers thành các package riêng biệt. Lời khuyên chung của tôi là chọn những form thích hợp nhất của việc phân tách (separation) cho vấn đề của bạn, nhưng chắc chắn rằng bạn làm một số loại separation, ít nhất là ở mức subroutine. Cùng với sự tách biệt (separation), cũng có một quy tắc ổn định (steady) về phụ thuộc: Các domain và data source không bao giờ nên phụ thuộc vào việc presentaion. Đó là, không nên có lời gọi subroutine từ code domain hoặc code data source tới presentaion code. Quy tắc này làm cho nó dễ dàng hơn để thay thế các presentations khác nhau trên cùng nền tảng và làm cho nó dễ dàng hơn để sửa đổi các presentation mà không có sự phân nhánh (ramifications) nghiêm trọng xuống sâu hơn. Mối quan hệ giữa domain và data source phức tạp hơn và phụ thuộc vào mô hình kiến trúc được sử dụng cho data source. Một trong những phần khó nhất làm việc với domain logic có vẻ là mọi người thường cảm thấy khó khăn để nhận ra domain logic là gì và các form của logic là gì. Một bài informal test tôi thích tưởng tượng bổ sung thêm một layer hoàn toàn khác tới một ứng dụng, chẳng hạn như một command-line inteface tới một Web application. Nếu có bất kỳ
  • 19. chức năng mà bạn phải lặp lại để làm điều này, đó là một dấu hiệu 'where domain logic has leaked into the presentation'. Tương tự như vậy, bạn có lặp logic để thay thế một relational database với một XML file? Một ví dụ điển hình của điều này là một hệ thống chứa một danh sách các sản phẩm mà trong đó tất cả các sản phẩm bán được hơn 10% so với họ đã bán trong tháng trước đó sẽ được sơn màu đỏ. Để làm được điều này các developer đặt logic trong presentation layer so sánh với doanh số của tháng này với tháng trước và nếu lệch hơn 10%, chúng được đặt màu đỏ. Vấn đề là điều đó đang đặt domain logic vào presentation. Để phân tách đúng các layers, bạn cần một method trong các domain layers để cho biết nếu một sản phẩm đã cải thiện doanh số bán hàng. Method này thực hiện so sánh giữa hai tháng và trả về một giá trị Boolean. Presentaion layer sau đó chỉ cần gọi method Boolean này và, nếu là true, highlights sản phẩm màu đỏ. Bằng cách đó process được chia (broken into) thành 2 parts: quyết định xem có cái gì đó có thể highlight và lựa chọn xem như thế nào để làm nổi bật (highlight). Choosing Where to Run Your Layers Nội dung chính của cuốn sách này sẽ nói về logic layers -- đó là, phân chia một hệ thống thành phần riêng biệt (separate pieces) để giảm coupling giữa các phần khác nhau của một hệ thống. Phân tách giữa các layers là hữu ích ngay cả khi các layers đều chạy trên một physical machine. Tuy nhiên, there are places where the physical structure of a system makes a difference. Đối với hầu hết các ứng dụng IS quyết định (decision) xem là để chạy processing trên một client, trên một desktop machine, hoặc trên một server. Thông thường các trường hợp đơn giản nhất là chạy tất cả mọi thứ trên servers. Một HTML front end sử dụng một Web browser là một cách tốt để làm điều này. Ưu điểm lớn nhất của việc chạy trên server là mọi thứ đều rất dễ dàng để nâng cấp (upgrade) và sửa chữa (fix) vì it's in a limited amount of places. Bạn không cần phải lo lắng về việc triển khai cho nhiều desktops và giữ cho chúng tất cả đồng bộ với server. Bạn không cần phải lo lắng về tương thích với các phần mềm desktop khác. Lập luận chung (the general argument) ủng hộ (favor) cho việc chạy trên một client tùy thuộc (turns on) vào responsiveness hoặc disconnected operation.. Bất kỳ logic mà chạy trên server cần một server roundtrip để đáp ứng với bất cứ điều gì user thực hiện. Nếu user muốn nghịch vớ vấn (fiddle with) cái gì đó và xem thông tin phản hồi ngay lập tức, that roundtrip gets in the way. Nó cũng cần có một kết nối mạng để chạy. The network may like to be everywhere, but as I type this it isn't at 31,000 feet. Nó có thể là ở khắp mọi nơi, nhưng có những người muốn làm việc bây giờ mà không đợi phủ sóng không dây để đạt Dead End Creek. Disconnected operation mang đến những thách thức đặc biệt, và tôi sợ tôi đã quyết định đưa những chúng ra khỏi phạm vi của cuốn sách này.
  • 20. Chúng ta có thể nhìn vào các tùy chọn layer by layer. Các data source khá nhiều luôn chỉ chạy trên các server. Các trường hợp ngoại lệ là nơi bạn có thể duplicate chức năng server vào một suitably powerful client, thông thường khi bạn muốn disconnected operation. Trong trường hợp này thay đổi data source trên client bị mất kết nối cần phải được đồng bộ hóa với server. Quyết định về nơi để chạy presentation phụ thuộc chủ yếu vào loại UI mà bạn muốn. Chạy một rich client much means chạy các presentation trên client. Chạy một Web interface much means chạy trên server. Có những ngoại lệ, for one, remote operation của phần mềm client (như máy chủ X trong thế giới Unix) chạy một Web server trên desktop nhưng những trường hợp ngoại lệ là rất hiếm. Nếu bạn đang xây dựng một hệ thống B2C, bạn không có sự lựa chọn. Bất kỳ Tom, Dick, hoặc Harriet có thể được kết nối với máy chủ của bạn và bạn không muốn biến bất cứ ai đi vì họ cứ khăng khăng mua thực hiện shopping online của họ với một TRS-80. Trong trường hợp này bạn làm tất cả các xử lý trên server và cung cấp lên HTML cho browser để deal (giao dịch). Hạn chế của bạn với các tùy chọn HTML là mỗi bit of decision cần một roundtripi từ client đến server, và có thể làm tổn thương (hurt) sự phản hồi (responsiveness). Bạn có thể làm giảm một trễ (some of lag) với browser scripting và downloadable appleti, nhưng chúng làm giảm khả năng tương thích browser của bạn và có xu hướng bổ sung thêm những vấn đề đau đầu khác. The more pure HTML you can go, the easier life is. That ease of life is appealing even if every one of your desktops is lovingly hand-built by your IS department. Giữ client cập nhật và tránh được các lỗi tương thích với các phần mềm khác là các vấn đề, ngay cả hệ thống rich-client đơn giản có. Lý do chính mà mọi người muốn có một rich-client presentation là một số nhiệm vụ khá phức tạp cho users để làm và, để có một ứng dụng có thể sử dụng, họ sẽ cần nhiều hơn những gì một Web GUI có thể cung cấp cho. Càng ngày, tuy nhiên, người ta thường sử dụng các để tạo Web front ends hữu ích hơn, và làm giảm sự cần thiết cho một rich client presentation. Khi tôi viết bài này tôi rất ủng hộ Web presentation nếu bạn có thể và rich client nếu bạn phải làm. Điều này để lại (leave) cho chúng ta với domain logic. Bạn có thể chạy các business logic tất cả trên server hoặc tất cả trên client, hoặc bạn có thể chia nó. Một lần nữa, tất cả trên server là sự lựa chọn tốt nhất để dễ bảo trì. Nhu cầu để di chuyển nó đến các client là cho hoặc responsiveness hoặc cho disconnected use. Nếu bạn phải chạy một số logic trên client, bạn có thể xem xét việc chạy tất cả nó ở đó -- at least that way it's all in one place. Thông thường điều này đi kèm (hand in hand) với một rich client -- đang chạy một Web server trên một client machine không phải để giúp responsiveness hơn, mặc dù nó có thể là một cách để đối phó với disconnected operation. Trong trường hợp này, bạn vẫn có thể giữ domain logic của bạn trong các modules riêng biệt từ presentation, với một Transaction Script hoặc một Domain Model. Vấn đề với việc đưa tất cả các domain logic trên client là bạn phải upgrade và maintain nhiều hơn.
  • 21. Splitting across cả desktop và server nghe giống như the worst of both worlds bởi vì bạn không biết where any piece of logic may be. Lý do chính để làm điều đó là bạn chỉ có một số lượng nhỏ của domain logic mà cần phải chạy trên client. Mẹo (trick) đó là cô lập (isolate) phần logic này trong một module khép kín (self-contained) mà không phụ thuộc vào bất kỳ một phần khác của hệ thống. Bằng cách đó bạn có thể chạy mà module trên client hoặc server. Một khi bạn đã chọn các nút xử lý của bạn (processing node), bạn nên cố gắng giữ tất cả code trong một single process, hoặc một node hoặc copied trên một vài nodes trong một cluster. Đừng cố gắng để tách các layers vào cá discrete (rời rạc) processes, trừ khi bạn phải làm. Làm như vậy sẽ làm giảm hiệu suất (degrade performance) và thêm phức tạp (add performance), như bạn phải thêm những thứ như Remote Facades và Data Transfer Objects. Điều quan trọng là phải nhớ rằng nhiều cái trong số những điều này là những gì Jens Coldewey gọi là phức tạp -- distribution, explicit multithreading, paradigm chasms (như object / relational), multiplatform development, và các yêu cầu extreme performance (như hơn 100 transactions/s ). Tất cả chúng đưa tới high cost (chi phí cao). Chắc chắn có những lúc bạn phải làm điều đó, nhưng không bao giờ quên rằng mỗi một cái mang một chi phí (charge) trong phát triển và bảo trì liên tục (on-going maintenance).
  • 22. 3 Chapter 2. Organizing Domain Logic Trong tổ chức Domain Logic, tôi đã phân tách nó thành 3 patterns chính: Transaction Script, Domain Model, và Table Module. Phương pháp đơn giản nhất để lưu domain logic là Transaction Script. Một Transaction Script bản chất là một thủ tục (procedures) lấy đầu vào từ presentation, xử lý nó với validations và calculations, lưu dữ liệu trong database, và gọi (invokes) các hoạt động bất kì từ các hệ thống khác. Sau đó nó trả lời (replies) với nhiều dữ liệu tới presentation, có lẽ thực hiện tính toán nhiều hơn để giúp tổ chức (organize) và định dạng (format) dữ liệu trả về (reply). Các tổ chức cơ bản (fundamental organization) là một thủ tục duy nhất (single procedure) cho mỗi action mà một user có thể muốn làm. Do đó, chúng ta có thể nghĩ về mô hình này như là một script cho một action, hoặc business transaction. Nó không phải là một single inline procedure của code. Các phần (pieces) được tách ra thành các chương trình con (subroutines), và các subroutines có thể được chia sẻ giữa Transaction Scripts khác nhau. Tuy nhiên, driving force vẫn là của một procedure cho mỗi action, do đó, một hệ thống bán lẻ có thể có Transaction Scripts cho checkout, cho adding something vào giỏ mua hàng, để hiển thị trạng thái giao hàng, ... Một Transaction Script có nhiều ưu điểm:  Đó là một mô hình thủ tục (procedural model) đơn giản mà hầu hết các developer hiểu.  Nó hoạt động tốt với một data source layer đơn giản bằng cách sử dụng Row Data Gateway hoặc Table Data Gateway.  Rõ ràng (obvious) làm thế nào để thiết lập các ranh giới giao dịch (transaction boundaries): Bắt đầu với việc mở một transaction và kết thúc với đóng cửa nó. Thật dễ dàng cho các tools để làm điều này đằng sau các scenes. Đáng buồn thay, Transaction Script cũng có nhiều nhược điểm, có xu hướng xuất hiện khi sự phức tạp của domain logic tăng. Thông thường code sẽ bị duplicated khi một số transactions cần làm những cái tương tự nhau (Often there will be duplicated code as several transactions need to do similar things). Một số (some of this) có thể được xử lý bằng cách tái cấu trúc (factoring out) ra các hàm chung (common subroutines), nhưng ngay cả như vậy nhiều trùng lặp code (duplication) là khó khăn để loại bỏ và khó nhận ra hơn. Ứng dụng đạt được (resulting application) có thể trở thành (end up) là một web khá rối với các routines không có một cấu trúc rõ ràng. Tất nhiên, logic phức tạp là nơi các objects đi vào (complex logic is where objects come in), và phương pháp (way) object-oriented để xử lý vấn đề này là với một Domain Model. Với một Domain Model, chúng tôi xây dựng một model của domain trong đó, ít nhất on a first approximation, được tổ chức chủ yếu xung quanh các danh từ trong domain. Do đó, một hệ thống cho thuê (leasing system) sẽ có các classes cho lease, asset, và ... Logic để validations và calculations sẽ được đặt vào domain model này, vì vậy đối tượng shipment có thể chứa các logic để tính toán shipping charge cho một delivery. Có thể vẫn là các rountines cho tính toán một hóa đơn, nhưng như vậy (but such) một procedure sẽ nhanh chóng delegate tới một method Domain Model.
  • 23. Sử dụng một Domain Model trái ngược với một Transaction Script là the essence of the paradigm shift that object-oriented people talk about so much. Thay vì một routine có tất cả các logic cho một action người dùng, mỗi object có một phần logic liên quan đến nó. Nếu bạn không sử dụng đến một Domain Model, học để làm việc với một cái có thể rất bực bội khi bạn vội vàng từ object tới object cố gắng tìm xem các behavior ở đâu (If you're not used to a Domain Model, learning to work with one can be very frustrating as you rush from object to object trying to find where the behavior is). Thật khó để nắm bắt được bản chất của sự khác biệt giữa hai patterns với một ví dụ đơn giản, nhưng trong thảo luận về các patterns tôi đã cố gắng để làm điều đó bằng cách xây dựng một pieces đơn giản của domain logic theo cả hai cách. Cách dễ nhất để thấy sự khác biệt là nhìn vào sequence diagrams cho hai phương pháp tiếp cận (hình 2.1 và 2.2). Vấn đề quan trọng là các loại product khác nhau có các thuật toán khác nhau để recognizing revenue trên một contract được đưa ra. Các method tính toán để xác định xem những loại product một contract được đưa ra là gì, cho việc áp dụng các thuật toán chính xác, và sau đó tạo ra các revenue recognition objects để nắm bắt được kết quả của phép tính. (Để đơn giản tôi bỏ qua các vấn đề tương tác cơ sở dữ liệu.)
  • 24. Trong hình 2.1, phương thức Transaction Script thực hiện mọi việc. Các đối tượng cơ sở (underlying objects) là Table Data Gateway, và tất cả chúng làm là pass data tới transaction script. Ngược lại, hình 2.2 cho nhiều đối tượng, từng phần chuyển tiếp (forwarding part) của behavior tới phần khác cho đến khi một strategy object tạo ra các kết quả. Giá trị của một Domain Model nằm trong thực tế rằng một khi bạn đã quen với điều đó, có rất nhiều kỹ thuật cho phép bạn xử lý logic ngày càng phức tạp theo một cách được tổ chức tốt. Khi chúng ta nhận được (get) nhiều hơn và nhiều hơn nữa các thuật toán để tính toán revenue recognition, chúng ta có thể thêm những cái này bằng cách thêm vào các đối tượng recognition strategy mới. Với Transaction Script, chúng ta đang bổ sung thêm nhiều điều kiện hơn tới các conditional logic của script. Một khi tâm trí bạn là 'warped to objects' như tôi, bạn sẽ thấy bạn thích một Domain Model ngay cả trong trường hợp khá đơn giản. Các chi phí của Domain Model đến từ sự phức tạp của việc sử dụng nó và sự phức tạp của data source layer của bạn. Phải mất thời gian cho những người mới tới các rich obect models để làm quen với một một rich Domain Model. Thường thì developers có thể phải bỏ ra nhiều tháng làm việc trên một dự án có sử dụng pattern này trước khi paradigms của họ được thay đổi. Tuy nhiên, khi bạn đã quen với Domain Model bạn thường infected (suy luận) cho cuộc sống và nó trở nên dễ dàng để làm việc trong tương lai. Tuy nhiên, một thiểu số đáng kể của các developers dường như là không thể thực hiện việc chuyển đổi. Ngay cả khi bạn đã thực hiện các thay đổi, bạn vẫn phải giải quyết (deal) với database mapping. The richer your Domain Model, the more complex your mapping to a relational database (usually with Data Mapper). Một data source layer phức tạp giống như một chi phí cố định – phải mất một số tiền (nếu bạn mua) hoặc thời gian (nếu bạn xây dựng), nhưng một khi bạn có nó, bạn có thể làm rất nhiều với nó. Có một lựa chọn thứ ba cho logic miền cấu trúc, Table Module. Với cái nhìn đầu tiên Table Module trông giống như một Domain Model vì cả hai đều có các classes cho các contracts, các products, và các revenue recognitions. Sự khác biệt quan trọng là một Domain Model có một instance của contract cho mỗi contract trong database trong khi một Table Module chỉ có một instance. Một Table Module được thiết kế để làm việc với một Record Set. Vì vậy, client của một contract Table Module đầu tiên sẽ phát hành (issue) các truy vấn tới database để tạo thành một Record Set và sẽ tạo ra một contract object và truyền (pass) nó Record Set như một đối số. Client có thể gọi các hoạt động về contract để làm những việc khác nhau (Hình 2.3). Nếu nó muốn làm một cái gì đó tới một contract riêng lẻ, nó phải truyền (pass) một ID.
  • 25. Một Table Module (trong nhiều cách) là một middle ground giữa một Transaction Script và một Domain Model. Tổ chức domain logic xung quanh các tables hơn là các straight procedures cung cấp cấu trúc hơn (more structure) và làm cho nó dễ dàng hơn để tìm và loại bỏ trùng lặp. Tuy nhiên, bạn không thể sử dụng một số kỹ thuật mà một Domain Model sử dụng cho cấu trúc 'finer grained' của logic, chẳng hạn như inheritance, strategies, và các patterns OO khác. Ưu điểm lớn nhất của một Table Module là làm thế nào nó phù hợp với phần còn lại của kiến trúc. Nhiều môi trường GUI được xây dựng để làm việc trên các kết quả của một truy vấn SQL được tổ chức trong một Record Set. Kể từ khi một Table Module cũng hoạt động trên một Record Set, bạn có thể dễ dàng chạy một truy vấn, thao tác các kết quả trong Table Module, và truyền các dữ liệu thao tác tới GUI để hiển thị. Bạn cũng có thể sử dụng Table Module trên way back cho validations và calculations. Một số nền tảng, đặc biệt là COM và .NET của Microsoft, sử dụng phong cách phát triển này. Making a Choice Làm thế nào để bạn lựa chọn giữa 3 patterns? Nó không phải là một lựa chọn dễ dàng, và nó phụ thuộc rất nhiều vào độ phức tạp domain logic của bạn thế nào. Hình 2.4 là một trong những đồ thị non-scientific mà thực sự kích thích tôi trong các bài thuyết trình PowerPoint. Tuy nhiên, nó giúp hình dung như thế nào so sánh 3 patterns. Với domain logic đơn giản Domain Model là kém hấp dẫn vì chi phí cho việc tìm hiểu và sự phức tạp của data source thêm rất nhiều effort để phát triển nó sẽ không được trả lại. Tuy nhiên, khi sự phức tạp của domain logic tăng lên, các phương pháp tiếp cận khác có xu hướng 'hit a wall' ở đó (where) thêm nhiều tính năng hơn theo cấp số nhân trở nên khó khăn hơn.
  • 26. Vấn đề của bạn, tất nhiên, là tìm ra ứng dụng của bạn nằm ở đâu trên x-axis. Tin tốt là tôi có thể nói rằng bạn nên sử dụng một Domain Model bất cứ khi nào sự phức tạp của domain logic của bạn là lớn hơn 7,42. Tin xấu là không ai biết làm thế nào để đo lường mức độ phức tạp của domain logic. Trong thực tế, tất cả bạn có thể làm là tìm thấy một số người có kinh nghiệm những người có thể làm một phân tích ban đầu các yêu cầu và thực hiện một judgment call. Có một số yếu tố làm thay đổi đường cong một chút. Một nhóm quen thuộc với Domain Model chi phải ban đầu sử dụng pattern này sẽ thấp hơn. The better the team is, the more I'm inclined to use a Domain Model. Sự hấp dẫn của một Table Module phụ thuộc rất nhiều vào sự hỗ trợ cho một cấu trúc Record Set common trong môi trường của bạn. Nếu bạn có một môi trường như .NET hoặc Visual Studio, nơi mà rất nhiều công cụ làm việc xung quanh một Record Set, thì điều đó làm Table Module hấp dẫn hơn nhiều. Thật vậy, tôi không thấy có lý do để sử dụng Transaction Scripts trong một môi trường .NET. Tuy nhiên, nếu không có tools đặc biệt cho Record Sets, tôi sẽ không bận tâm với Table Module. Một khi bạn đã thực hiện nó, quyết định của bạn không hoàn toàn được đúc bằng đá (cast in stone), nhưng nó là khó khăn hơn để thay đổi. So it's worth some upfront thought to decide which way to go. Nếu bạn nhận thấy bạn đã đi sai đường, thì, nếu bạn đã bắt đầu với Transaction Script (110), không ngần ngại cấu trúc lại theo hướng Domain Model. Nếu bạn đã bắt đầu với Domain Model, tuy nhiên, đi tới (going to) Transaction Script thường ít đáng giá, trừ khi bạn có thể đơn giản hóa data source layer của bạn.
  • 27. These three patterns are not mutually exclusive choices. Thật vậy, nó khá phổ biến để sử dụng Transaction Script cho một số domain logic và Table Module hoặc Domain Model cho phần còn lại. Service Layer Một cách tiếp cận phổ biến trong xử lý domain logic là phân chia các domain layer thành 2 (in two). Một Service Layer được đặt trên (placed over) một Domain Model cơ bản hoặc Table Module. Thông thường, bạn chỉ nhận được điều này với một Domain Model hoặc Table Module khi (since) một domain layer mà chỉ sử dụng Transaction Script là không đủ phức tạp để đảm bảo một layer riêng biệt. Presentation logic tương tác với domain hoàn toàn thông qua Service Layer, hoạt động như một API cho ứng dụng. Cũng như cung cấp một API rõ ràng, Service Layer cũng là một điểm tốt (good spot) để đặt những thứ như transaction control và security. Điều này cung cấp cho bạn một model đơn giản của taking each method trong Service Layer và mô tả đặc điểm transaction và security của nó. Một file thuộc tính riêng biệt là một lựa chọn phổ biến cho điều này, nhưng .NET attributes cung cấp một cách tốt làm trực tiếp trong code. Khi bạn nhìn thấy một Service Layer, một quyết định quan trọng là có bao nhiêu behavior đặt vào nó (how many behavior to put in it). Trường hợp tối thiểu là làm cho Service Layer một facade để tất cả các real behavior là trong underlying objects và tất cả các Service Layer làm là chuyển tiếp cuộc gọi (calls) trên facade tới các object mức thấp hơn. Trong trường hợp đó các Service Layer cung cấp một API dễ dàng hơn để sử dụng vì nó thường hướng đến xung quanh các use cases. Nó cũng làm cho một điểm thuận tiện cho việc thêm transactional wrappers và kiểm tra security. Ở một thái cực khác (other extreme), hầu hết các business logic được đặt trong Transaction Scripts bên trong các Service Layer. Các underlying domain objects rất đơn giản; nếu đó là một Domain Model nó sẽ là one-to-one với database và do đó bạn có thể sử dụng một data source layer đơn giản hơn như Active Record. Midway between these alternatives is a more even mix of behavior: the controller-entity style. Tên này xuất phát từ một thực tế phổ biến ảnh hưởng nặng nề bởi [Jacobson et al.]. Vấn đề ở đây là có logic đặc biệt cho một transaction duy nhất hoặc use case đặt trong Transaction Scripts, thường được referred tới như các controllers hoặc services. Đây là các controllers khác nhau với input controller trong Model View Controller hoặc Application Controller (These are different controllers to the input controller in Model View Controller or Application Controller) mà chúng ta sẽ gặp sau này, vì vậy tôi sử dụng thuật ngữ use-case controller. Behavior đó được sử dụng trong nhiều use case đi vào các domain objects, được gọi là entities. Mặc dù phương pháp controller-entity là một phương pháp phổ biến, nó không phải là một phương pháp mà tôi đã từng thích nhiều. Các use case controllers, giống như bất kỳ Transaction Script giao dịch, có xu hướng ủng hộ (encourage) duplicate code. Quan điểm của tôi là, nếu bạn quyết định sử dụng một Domain Model ở tất cả, bạn thực sự nên go
  • 28. whole hog (cắt ngắn) and make it dominant (trọng yếu). Một ngoại lệ là nếu bạn đã bắt đầu với một thiết kế có sử dụng Transaction Script với Row Data Gateway. Sau đó, nó tạo cảm giác move duplicated behavior tới các Row Data Gateways, mà sẽ biến (turn) chúng thành một Domain Model đơn giản sử dụng Active Record. Tuy nhiên, tôi sẽ không bắt đầu theo cách đó. Tôi sẽ chỉ làm điều đó để cải thiện thiết kế mà đang có dấu hiệu cracks. Tôi nói không phải là bạn không bao giờ nên có các service objects có chứa business logic, nhưng mà bạn không nên nhất thiết phải làm cho một layer cố định của chúng. Các procedural service objects đôi khi có thể là một cách rất hữu ích để factor logic, nhưng tôi có xu hướng sử dụng chúng khi cần thiết chứ không phải là một architectural layer. Sự ưa thích của tôi là có các Service Layer mỏng nhất (the thinnest) bạn có thể, nếu bạn thậm chí cần một. Cách tiếp cận thông thường của tôi là giả sử tôi không cần một và chỉ thêm nó nếu có vẻ ứng dụng cần đến nó. Tuy nhiên, tôi biết nhiều nhà thiết kế tốt, những người luôn luôn sử dụng một Service Layer với một fair bit of logic. Randy Stafford đã có rất nhiều thành công với một rich Service Layer, đó là lý do tại sao tôi yêu cầu ông viết Service Layer pattern cho cuốn sách này.