Tại sao phải tách ra truy cập dữ liệu?
Từ cuốn sách, tôi nghĩ hai trang đầu tiên của chương Thiết kế hướng mẫu cho một số lý do tại sao bạn muốn trừu tượng hóa các chi tiết triển khai kỹ thuật từ việc triển khai mô hình miền.
- Bạn muốn giữ một kết nối chặt chẽ giữa mô hình miền và mã
- Phân tách mối quan tâm kỹ thuật giúp chứng minh mô hình là thiết thực để thực hiện
- Bạn muốn ngôn ngữ có mặt khắp nơi thấm vào thiết kế của hệ thống
Đây dường như là tất cả cho mục đích tránh một "mô hình phân tích" riêng biệt trở nên ly dị với việc triển khai thực tế của hệ thống.
Từ những gì tôi hiểu về cuốn sách, nó nói rằng "mô hình phân tích" này cuối cùng có thể được thiết kế mà không cần xem xét triển khai phần mềm. Một khi các nhà phát triển cố gắng thực hiện mô hình được hiểu bởi phía doanh nghiệp, họ hình thành sự trừu tượng của riêng họ do sự cần thiết, gây ra một bức tường trong giao tiếp và hiểu biết.
Theo hướng khác, các nhà phát triển đưa quá nhiều mối quan tâm kỹ thuật vào mô hình miền cũng có thể gây ra sự phân chia này.
Vì vậy, bạn có thể xem xét rằng thực hành phân tách các mối quan tâm như sự kiên trì có thể giúp bảo vệ chống lại các thiết kế này một mô hình phân tích phân kỳ. Nếu cảm thấy cần thiết phải giới thiệu những thứ như sự kiên trì vào mô hình thì đó là một lá cờ đỏ. Có thể mô hình không thực tế để thực hiện.
Trích dẫn:
"Mô hình duy nhất làm giảm khả năng xảy ra lỗi, vì thiết kế hiện là kết quả trực tiếp của mô hình được xem xét cẩn thận. Thiết kế, và thậm chí chính mã, có khả năng giao tiếp của mô hình."
Cách tôi diễn giải điều này, nếu bạn kết thúc với nhiều dòng mã xử lý những thứ như truy cập cơ sở dữ liệu, bạn sẽ mất khả năng giao tiếp đó.
Nếu nhu cầu truy cập cơ sở dữ liệu là dành cho những việc như kiểm tra tính duy nhất, hãy xem:
Udi Dahan: những sai lầm lớn nhất mà các đội mắc phải khi áp dụng DDD
http://gojko.net/2010/06/11/udi-dahan-the-biggest-mistakes-teams-make-when-appending-ddd/
trong "Tất cả các quy tắc không được tạo ra bằng nhau"
và
Sử dụng mẫu mô hình miền
http://msdn.microsoft.com/en-us/magazine/ee236415.aspx#id0400119
trong "Kịch bản không sử dụng mô hình miền", chạm vào cùng một chủ đề.
Cách tách quyền truy cập dữ liệu
Đang tải dữ liệu qua giao diện
"Lớp truy cập dữ liệu" đã được trừu tượng hóa thông qua một giao diện mà bạn gọi để lấy dữ liệu cần thiết:
var orderLines = OrderRepository.GetOrderLines(orderId);
foreach (var line in orderLines)
{
total += line.Price;
}
Ưu điểm: Giao diện tách mã hệ thống ống nước "truy cập dữ liệu", cho phép bạn vẫn viết bài kiểm tra. Truy cập dữ liệu có thể được xử lý theo từng trường hợp cho phép hiệu suất tốt hơn so với chiến lược chung.
Nhược điểm: Mã gọi phải giả định những gì đã được tải và những gì chưa.
Nói GetOrderLines trả về các đối tượng OrderLine với thuộc tính ProductInfo null vì lý do hiệu năng. Nhà phát triển phải có kiến thức sâu sắc về mã đằng sau giao diện.
Tôi đã thử phương pháp này trên các hệ thống thực. Cuối cùng, bạn thay đổi phạm vi của những gì được tải mọi lúc để cố gắng khắc phục các sự cố về hiệu suất. Cuối cùng, bạn nhìn trộm phía sau giao diện để xem mã truy cập dữ liệu để xem những gì đang và không được tải.
Bây giờ, việc phân tách các mối quan tâm sẽ cho phép nhà phát triển tập trung vào một khía cạnh của mã cùng một lúc, càng nhiều càng tốt. Kỹ thuật giao diện loại bỏ CÁCH dữ liệu này được tải, nhưng không tải dữ liệu NHIỀU, KHI được tải và KHI được tải ở đâu.
Kết luận: Khá tách biệt!
Tải lười biếng
Dữ liệu được tải theo yêu cầu. Các lệnh gọi để tải dữ liệu được ẩn trong chính biểu đồ đối tượng, trong đó việc truy cập một thuộc tính có thể khiến truy vấn sql thực thi trước khi trả về kết quả.
foreach (var line in order.OrderLines)
{
total += line.Price;
}
Ưu điểm: Quyền truy cập dữ liệu 'WHEN, WHERE và CÁCH' bị ẩn khỏi nhà phát triển tập trung vào logic miền. Không có mã nào trong tổng hợp liên quan đến việc tải dữ liệu. Lượng dữ liệu được tải có thể là số lượng chính xác theo yêu cầu của mã.
Nhược điểm: Khi bạn gặp phải vấn đề về hiệu năng, thật khó để khắc phục khi bạn có giải pháp "một kích thước phù hợp với tất cả" chung chung. Tải chậm có thể gây ra hiệu suất tổng thể tồi tệ hơn và thực hiện tải lười biếng có thể khó khăn.
Giao diện vai trò / Tìm nạp háo hức
Mỗi trường hợp sử dụng được thực hiện rõ ràng thông qua Giao diện vai trò được thực hiện bởi lớp tổng hợp, cho phép các chiến lược tải dữ liệu được xử lý cho mỗi trường hợp sử dụng.
Chiến lược tìm nạp có thể trông như thế này:
public class BillOrderFetchingStrategy : ILoadDataFor<IBillOrder, Order>
{
Order Load(string aggregateId)
{
var order = new Order();
order.Data = GetOrderLinesWithPrice(aggregateId);
return order;
}
}
Sau đó, tổng hợp của bạn có thể trông như:
public class Order : IBillOrder
{
void BillOrder(BillOrderCommand command)
{
foreach (var line in this.Data.OrderLines)
{
total += line.Price;
}
etc...
}
}
BillOrderFetchingStrargety được sử dụng để xây dựng tổng hợp, và sau đó tổng hợp thực hiện công việc của nó.
Ưu điểm: Cho phép mã tùy chỉnh cho mỗi trường hợp sử dụng, cho phép hiệu suất tối ưu. Là nội tuyến với Nguyên tắc phân chia giao diện . Không có yêu cầu mã phức tạp. Các bài kiểm tra đơn vị tổng hợp không phải bắt chước chiến lược tải. Chiến lược tải chung có thể được sử dụng cho hầu hết các trường hợp (ví dụ: chiến lược "tải tất cả") và chiến lược tải đặc biệt có thể được thực hiện khi cần thiết.
Nhược điểm: Nhà phát triển vẫn phải điều chỉnh / xem lại chiến lược tìm nạp sau khi thay đổi mã miền.
Với cách tiếp cận chiến lược tìm nạp, bạn vẫn có thể thấy mình thay đổi mã tìm nạp tùy chỉnh để thay đổi quy tắc kinh doanh. Nó không phải là một sự tách biệt hoàn hảo của các mối quan tâm nhưng cuối cùng sẽ dễ bảo trì hơn và tốt hơn so với lựa chọn đầu tiên. Chiến lược tìm nạp sẽ gói gọn dữ liệu CÁCH, KHI và WHERE được tải. Nó có sự phân tách mối quan tâm tốt hơn, mà không mất tính linh hoạt như một kích thước phù hợp với tất cả các phương pháp tải lười biếng.