7

Câu hỏi này là ngôn ngữ bất khả tri nhưng tôi là người C# nên tôi sử dụng thuật ngữ POCO để chỉ một đối tượng chỉ lưu trữ dữ liệu trước, thường sử dụng các trường getter và setter.Làm cách nào để tách xác thực dữ liệu khỏi các đối tượng miền đơn giản của tôi (POCO)?

Tôi vừa mới làm lại Mô hình miền của mình để trở thành siêu tối ưu POCO và còn lại với một vài lo ngại về cách đảm bảo rằng giá trị thuộc tính có ý nghĩa với miền.

Ví dụ: EndDate của Dịch vụ không được vượt quá EndDate của Hợp đồng mà Dịch vụ đang ở dưới. Tuy nhiên, nó có vẻ như một sự vi phạm của SOLID để đặt kiểm tra vào Service.EndDate setter, chưa kể rằng khi số lượng xác nhận cần được thực hiện phát triển các lớp POCO của tôi sẽ trở nên lộn xộn.

Tôi có một số giải pháp (sẽ đăng trong câu trả lời), nhưng họ có những nhược điểm của họ và tự hỏi một số phương pháp ưa thích để giải quyết tình thế khó xử này là gì?

Trả lời

6

Tôi nghĩ rằng bạn đang bắt đầu với một giả định xấu, nghĩa là, bạn nên có các đối tượng không làm gì ngoài lưu trữ dữ liệu và không có phương thức nào ngoài accessors.Toàn bộ điểm có đối tượng là để đóng gói dữ liệu và hành vi. Nếu bạn có một thứ mà về cơ bản, một cấu trúc, bạn đang đóng gói những hành vi nào?

+1

Phải, các đối tượng này không đóng gói hành vi. Đó là toàn bộ vấn đề. Chúng đại diện cho miền của tôi. Đó là nó. Hành vi được cung cấp ở nơi khác. –

+0

Miền của bạn không có hành vi? –

+1

Mô hình miền không có hành vi. hành vi được gắn liền với chúng một cách riêng biệt. Dịch vụ thực hiện các công cụ bằng cách sử dụng các mô hình. Ít nhất đó là đọc của tôi về toàn bộ điều DDD. –

0

Tôi nghĩ rằng đó có lẽ sẽ là nơi tốt nhất cho logic, thực sự, nhưng đó chỉ là tôi. Bạn có thể có một số phương thức IsValid để kiểm tra tất cả các điều kiện và trả về true/false, có thể một số loại ErrorMessages nhưng đó là một chủ đề không rõ ràng vì các thông báo lỗi không thực sự là một phần của Domain Model. Tôi hơi thiên vị khi tôi đã làm một số công việc với RoR và đó là cơ bản những gì các mô hình của nó làm.

+0

SRP - là mô hình của bạn chịu trách nhiệm đại diện cho một đối tượng thế giới thực hoặc để xác thực đầu vào? Gotta được Ruthless trong này –

3

Tôi luôn luôn nghe đối số của mọi người về phương thức "Xác thực" hoặc "IsValid".

Cá nhân tôi nghĩ điều này có thể hiệu quả, nhưng với hầu hết các dự án DDD bạn thường kết thúc với nhiều xác thực được cho phép tùy thuộc vào trạng thái cụ thể của đối tượng.

Vì vậy, tôi thích "IsValidForNewContract", "IsValidForTermination" hoặc tương tự, vì tôi tin rằng hầu hết các dự án kết thúc với nhiều trình xác nhận/trạng thái như vậy trên mỗi lớp. Điều đó cũng có nghĩa là tôi không có giao diện, nhưng tôi có thể viết trình xác thực tổng hợp mà đọc phản ánh rất tốt điều kiện kinh doanh mà tôi đang khẳng định.

Tôi thực sự tin rằng các giải pháp chung trong trường hợp này rất thường tập trung đi từ những gì quan trọng - mã đang làm gì - cho một sự gia tăng rất nhỏ về sự sang trọng kỹ thuật (giao diện, đại biểu hoặc bất kỳ thứ gì). Chỉ cần bỏ phiếu cho tôi xuống cho nó;)

2

Một giải pháp là phải có DataAccessObject của mỗi đối tượng có một danh sách các Trình xác thực. Khi Lưu được gọi là biểu mẫu sẽ kiểm tra trước mỗi trình xác thực:

public class ServiceEndDateValidator : IValidator<Service> { 
    public void Check(Service s) { 
    if(s.EndDate > s.Contract.EndDate) 
     throw new InvalidOperationException(); 
    } 
} 

public class ServiceDao : IDao<Service> { 
    IValidator<Service> _validators; 
    public ServiceDao(IEnumerable<IValidator<Service>> validators) {_validators = validators;} 
    public void Save(Service s) { 
    foreach(var v in _validators) 
     v.Check(service); 
    // Go on to save 
    } 
} 

Lợi ích, rất rõ ràng SoC, bất lợi là chúng tôi không nhận được séc cho đến khi Save() được gọi.

0

Một khả năng khác là phải có mỗi lớp học của tôi thực hiện

public interface Validatable<T> { 
    public event Action<T> RequiresValidation; 
} 

Và có mỗi setter cho mỗi lớp học nâng cao sự kiện trước khi cài đặt (có lẽ tôi có thể đạt được điều này thông qua các thuộc tính).

Ưu điểm là kiểm tra xác thực theo thời gian thực. Nhưng mã lộn xộn hơn và không rõ ai nên làm việc đính kèm.

2

Trong quá khứ, tôi thường ủy quyền xác thực dịch vụ cho riêng mình, chẳng hạn như ValidationService. Điều này về nguyên tắc vẫn còn quảng cáo nghe đến triết lý của DDD.

Nội bộ này sẽ chứa bộ sưu tập Trình xác thực và tập hợp các phương thức công khai đơn giản như Validate() có thể trả về tập hợp đối tượng lỗi.

Rất đơn giản, một cái gì đó như thế này trong C#

public class ValidationService<T> 
{ 
    private IList<IValidator> _validators; 

    public IList<Error> Validate(T objectToValidate) 
    { 
    foreach(IValidator validator in _validators) 
    { 
     yield return validator.Validate(objectToValidate); 
    } 
    } 
} 

Validators có thể một trong hai được thêm vào trong một constructor mặc định hoặc tiêm qua một số lớp khác như một ValidationServiceFactory.

+0

Sẽ không tạo phân cấp song song http://stackoverflow.com/questions/778449/code-smell-or-not-validators-and-models-share-same-kind-of-hiereachy – Surya

+0

Surya, bạn có thể giải thích những gì bạn có nghĩa là gì? Tôi sẽ cố giải thích thêm ... – Xian

0

Đây là một khả năng khác. Xác thực được thực hiện thông qua proxy hoặc trang trí trên đối tượng Miền:

public class ServiceValidationProxy : Service { 
    public override DateTime EndDate { 
    get {return EndDate;} 
    set { 
     if(value > Contract.EndDate) 
     throw new InvalidOperationexception(); 
     base.EndDate = value; 
    } 
    } 
} 

Ưu điểm: Xác thực ngay lập tức. Có thể dễ dàng được cấu hình thông qua một IoC.

Bất lợi: Nếu proxy, thuộc tính được xác thực phải là ảo, nếu trang trí tất cả các mô hình miền phải dựa trên giao diện. Các lớp xác nhận sẽ kết thúc một chút nặng - proxys phải kế thừa lớp và trang trí phải thực hiện tất cả các phương thức. Đặt tên và tổ chức có thể gây nhầm lẫn.

3

Một đồng nghiệp của tôi đã đưa ra ý tưởng phát triển khá tốt. Chúng tôi không bao giờ đưa ra một cái tên tuyệt vời cho nó nhưng chúng tôi gọi nó là Thanh tra/Thẩm phán.

Thanh tra sẽ xem xét một đối tượng và cho bạn biết tất cả các quy tắc mà nó vi phạm. Thẩm phán sẽ quyết định làm gì về nó. Sự phân chia này cho phép chúng ta làm một vài điều. Nó cho phép chúng ta đặt tất cả các quy tắc ở một nơi (Thanh tra) nhưng chúng ta có thể có nhiều Thẩm phán và chọn Thẩm phán theo ngữ cảnh.

Một ví dụ về việc sử dụng nhiều Thẩm phán xoay quanh quy tắc cho biết Khách hàng phải có Địa chỉ. Đây là một ứng dụng ba cấp tiêu chuẩn. Trong tầng UI, Thẩm phán sẽ tạo ra thứ gì đó mà UI có thể sử dụng để chỉ ra các trường phải được điền. UI Judge không ném ngoại lệ. Trong lớp dịch vụ có một Thẩm Phán khác. Nếu nó tìm thấy một khách hàng mà không có một địa chỉ trong quá trình lưu nó sẽ ném một ngoại lệ. Tại thời điểm đó bạn thực sự phải dừng mọi thứ từ khi tiếp tục.

Chúng tôi cũng có các Thẩm phán nghiêm ngặt hơn khi trạng thái của các đối tượng thay đổi. Đó là một ứng dụng bảo hiểm và trong quá trình Quoting một Chính sách được phép được lưu trong trạng thái không đầy đủ. Nhưng một khi Chính sách đó đã sẵn sàng để được kích hoạt, nhiều thứ phải được thiết lập. Vì vậy, thẩm phán bỏ phiếu ở phía bên dịch vụ không nghiêm ngặt như Thẩm phán kích hoạt. Tuy nhiên, các quy tắc được sử dụng trong Thanh tra vẫn như cũ nên bạn vẫn có thể nói những gì chưa hoàn thành ngay cả khi bạn quyết định không làm bất cứ điều gì về nó.

Các vấn đề liên quan