Đừng lo lắng về nguyên tắc trách nhiệm duy nhất. Điều này sẽ không giúp bạn đưa ra quyết định tốt ở đây vì bạn có thể chủ quan chọn một khái niệm cụ thể là "trách nhiệm". Bạn có thể nói trách nhiệm của lớp là quản lý tính bền vững của dữ liệu đối với cơ sở dữ liệu hoặc bạn có thể nói trách nhiệm của lớp là thực hiện tất cả các công việc liên quan đến việc tạo người dùng. Đây chỉ là các cấp độ khác nhau trong hành vi của ứng dụng và cả hai đều là biểu hiện khái niệm hợp lệ của một "trách nhiệm duy nhất". Vì vậy, nguyên tắc này là không có ích cho việc giải quyết vấn đề của bạn.
Nguyên tắc hữu ích nhất để áp dụng trong trường hợp này là nguyên tắc ít gây bất ngờ nhất . Vì vậy, hãy đặt câu hỏi: có ngạc nhiên khi một kho lưu trữ với vai trò chính là lưu trữ dữ liệu vào cơ sở dữ liệu cũng gửi e-mail không?
Vâng, nó rất nhiều là đáng ngạc nhiên. Đây là hai hệ thống bên ngoài hoàn toàn riêng biệt và tên SaveChanges
này cũng không ngụ ý gửi thông báo. Việc bạn ủy thác điều này cho một sự kiện làm cho hành vi trở nên đáng ngạc nhiên hơn, vì ai đó đang đọc mã có thể không còn dễ dàng nhìn thấy những hành vi bổ sung nào được gọi. Vô cảm gây hại cho khả năng đọc. Đôi khi, các lợi ích đáng giá cho chi phí dễ đọc, nhưng không phải khi bạn tự động gọi thêm một hệ thống bên ngoài có tác dụng quan sát được cho người dùng cuối. (Logging thể được loại trừ ở đây kể từ khi tác dụng của nó là về cơ bản hồ sơ lưu giữ cho mục đích gỡ lỗi. End người dùng không tiêu thụ nhật ký, vì vậy không có hại trong luôn đăng nhập.) Thậm chí tệ hơn, điều này làm giảm tính linh hoạt trong thời gian gửi e-mail, khiến cho không thể xen kẽ các hoạt động khác giữa lưu và thông báo.
Nếu mã của bạn thường cần gửi thông báo khi người dùng được tạo thành công, bạn có thể tạo một phương thức như vậy:
public void AddUserAndNotify(IUserRepository repo, IEmailNotification notifier, MyUser user)
{
repo.Add(user);
repo.SaveChanges();
notifier.SendUserCreatedNotification(user);
}
Nhưng việc này có làm tăng giá trị hay không phụ thuộc vào chi tiết cụ thể của ứng dụng của bạn.
Tôi thực sự không khuyến khích sự tồn tại của SaveChanges
phương pháp này. Phương pháp này có thể sẽ cam kết một giao dịch cơ sở dữ liệu, nhưng các kho lưu trữ khác có thể đã sửa đổi cơ sở dữ liệu trong cùng một giao dịch . Thực tế là nó cam kết tất cả chúng là một lần nữa đáng ngạc nhiên, vì SaveChanges
đặc biệt gắn liền với trường hợp này của kho lưu trữ người dùng.
Mẫu đơn giản nhất để quản lý giao dịch cơ sở dữ liệu là một using
khối bên ngoài :
using (DataContext context = new DataContext())
{
_userRepository.Add(context, user);
context.SaveChanges();
notifier.SendUserCreatedNotification(user);
}
Điều này cho phép lập trình viên kiểm soát rõ ràng khi thay đổi tất cả các kho lưu trữ, buộc mã phải ghi lại một cách rõ ràng chuỗi các sự kiện phải xảy ra trước khi cam kết, đảm bảo khôi phục được đưa ra do lỗi (giả sử rằng có sự DataContext.Dispose
cố quay ngược lại) và tránh bị ẩn kết nối giữa các lớp nhà nước.
Tôi cũng không muốn gửi e-mail trực tiếp trong yêu cầu. Sẽ mạnh mẽ hơn khi ghi lại nhu cầu thông báo trong hàng đợi. Điều này sẽ cho phép xử lý thất bại tốt hơn. Đặc biệt, nếu xảy ra lỗi khi gửi e-mail, nó có thể được thử lại sau mà không làm gián đoạn việc lưu người dùng và tránh trường hợp người dùng được tạo nhưng lỗi được trang web trả về.
using (DataContext context = new DataContext())
{
_userRepository.Add(context, user);
_emailNotificationQueue.AddUserCreateNotification(user);
_emailNotificationQueue.Commit();
context.SaveChanges();
}
Trước tiên, tốt nhất là cam kết hàng đợi thông báo vì người tiêu dùng của hàng đợi có thể xác minh rằng người dùng tồn tại trước khi gửi e-mail, trong trường hợp context.SaveChanges()
cuộc gọi thất bại. (Nếu không, bạn sẽ cần một chiến lược cam kết hai giai đoạn đầy đủ để tránh heurrbugs.)
Điểm mấu chốt là phải thực tế. Thực tế suy nghĩ thông qua các hậu quả (cả về rủi ro và lợi ích) của việc viết mã theo một cách cụ thể. Tôi thấy rằng "nguyên tắc trách nhiệm duy nhất" không thường xuyên giúp tôi làm điều đó, trong khi "nguyên tắc ít bất ngờ nhất" thường giúp tôi đi vào đầu một nhà phát triển khác (có thể nói) và suy nghĩ về những gì có thể xảy ra.