2013-04-05 26 views
9

Trong DDD có thể tổng hợp bất biến bao gồm một quy tắc dựa trên thông tin trong một tổng hợp khác? Bây giờ tôi không nghĩ như vậy, tuy nhiên điều này gây ra cho tôi một vấn đề và tôi không biết làm thế nào để giải quyết nó.Có thể tổng hợp bất biến bao gồm quy tắc dựa trên thông tin từ nơi khác không?

Tôi có một thực thể được gọi là Tài sản (thiết bị) mà tôi đang lập mô hình làm gốc của tổng hợp. Nó có một danh sách các thẻ (thuộc tính) mô tả những thứ như Nhà sản xuất, Mô hình vv. Nó lưu trữ danh tính của tổng hợp thứ hai được gọi là AssetType có danh sách các TagTypes, trong đó một số có thể được đánh dấu là bắt buộc.

Bây giờ có vẻ như với tôi rằng một trong những điều kiện bất biến đối với Nội dung nên tham chiếu đến AssetType được liên kết để thực thi các giá trị không null trong danh sách các thẻ bắt buộc. Nhưng ruột của tôi đang bò với ý nghĩ về cách tôi sẽ thực thi tính nhất quán.

Điều này có nghĩa là tổng hợp nên thực sự bao gồm tất cả bốn thực thể? Nếu root là AssetType và nó có danh sách Assets dưới nó, nó có thể giải quyết được vấn đề của tôi, tuy nhiên điều này sẽ không phù hợp lắm với trường hợp sử dụng lõi có các tập hợp khác duy trì danh sách các loại tài sản khác nhau. Tài sản thực sự phải là gốc nếu không tôi sẽ gặp vấn đề.

Và AssetType cũng không thể đi vào bên trong tập hợp nội dung. Điều này có vẻ như là vô lý.

Ruột của tôi vẫn nói Asset và AssetType là hai tập hợp riêng biệt, nhưng làm cách nào để giải quyết các vấn đề nhất quán? Hay tôi có sai lầm bất biến của tôi?

Trả lời

4

Trong tình huống này, có một số phương pháp để thực thi bất biến.

Trước hết, hãy xem xét các hành vi xung quanh tổng hợp Asset. Tôi cho rằng có ít nhất một số CreateAssetCommand và một số RemoveTagCommand. Các bất biến nên được áp dụng trong quá trình thực hiện các lệnh này theo cách sau:

CreateAssetCommand

Kể từ khi một tài sản luôn gắn liền với một loại tài sản, một AssetTypeId phải được cung cấp như một phần của lệnh này. ID này phải được người gọi thu được, có thể bằng cách tra cứu một loại nội dung cụ thể. Khi AssetType được tìm kiếm, các đối tượng tương ứng TagType cũng có thể được truy xuất, các mục bắt buộc cụ thể. Điều này sẽ cho phép người gọi xây dựng các yêu cầu Tag trường hợp để gửi như một phần của lệnh. Lưu ý, người gọi có trách nhiệm cung cấp loại và thẻ nội dung hợp lệ.

RemoveTagCommand

Các handler cho lệnh này có thể lấy lại thích hợp Asset mà các cửa hàng AssetTypeId. Tiếp theo, trình xử lý sẽ truy lục tập hợp các thẻ bắt buộc cho loại nội dung và đảm bảo rằng các thẻ đó không bị xóa. Trong trường hợp này, bất biến được thực thi bởi chính trình điều khiển.

Một cách khác để xử lý những bất biến này là giới thiệu eventual consistency, nếu được chấp nhận. Với phương pháp này, việc xóa thẻ khỏi nội dung phải xuất bản TagRemovedEvent. Sau đó, trình xử lý cho sự kiện này có thể xác minh rằng thẻ bắt buộc đã không bị xóa. Nếu có, nó có thể tạo ra một nhiệm vụ hoặc thông báo nói rằng một tài sản đang ở trạng thái không hợp lệ. Lưu ý, điều này giả định rằng có thể chấp nhận cho một nội dung ở trạng thái không hợp lệ cho đến khi có điều gì đó được sửa chữa.

Hiện hành vi xung quanh AssetType. Một lệnh có thể thỏa hiệp tính toàn vẹn của tổng hợp Asset là việc giới thiệu một bắt buộc mới Tag. Trong trường hợp này, cách onyl để đảm bảo tính toàn vẹn là tạo các thẻ thích hợp cho từng nội dung tương ứng. Vì điều này có khả năng không thể được thực hiện tự động, sự nhất quán cuối cùng phải được chấp nhận cho đến khi các thẻ thích hợp được cung cấp thông qua can thiệp thủ công.

Với tất cả các phương pháp này, bạn không có loại tính toàn vẹn mà bạn có được với RDMS. Trách nhiệm của việc thực thi các bất biến tổng hợp chéo được giao cho các trình xử lý lệnh, trình xử lý sự kiện và mã gọi. Tuy nhiên, trong nhiều trường hợp, loại tính nhất quán này hoàn toàn có thể chấp nhận được.

Hãy xem Effective Aggregate Design để biết thêm về điều này.

+0

Cảm ơn bạn đã trả lời. Tôi nghĩ sự nhất quán cuối cùng là câu trả lời như bạn đã nói. Mặc dù @GiacomoTesio đã cung cấp trợ giúp tuyệt vời cũng – MJM

+0

Tôi cũng chỉ ra cho những người khác đọc điều này, rằng bản năng suy ra điều kiện là tất cả quá dễ dàng. Như chúng tôi đã phát hiện ra các sự kiện thực tế là tài sản được tạo ra từ AssteTypes nhưng sau đó, nhiều hay ít, rời rạc khỏi Type gốc. Tham chiếu có thể được thực hiện trở lại tại bất kỳ thời điểm nào đối với đặc tả ban đầu của Nội dung và kết quả được cập nhật theo nghĩa, nhưng hành động này được gọi bởi người gọi chứ không phải là tác dụng phụ của việc lưu Loại gốc. Nhận thức này đã giúp rất nhiều. – MJM

4

Có thể tổng hợp bất biến bao gồm quy tắc dựa trên thông tin từ nơi khác không?

Tổng hợp luôn có thể sử dụng thông tin ở trạng thái của riêng mình và đối số rằng số commands của họ sẽ nhận được.

Một người nào đó sử dụng để truy cập các dịch vụ ứng dụng thông qua người độc thân, người định vị dịch vụ vv, nhưng IMO, đó là mùi của các ứng dụng được kết hợp chặt chẽ. Họ quên rằng các đối số của phương pháp là các bộ phận phụ thuộc hiệu quả! :-)

Trong DDD có thể tổng hợp bất biến bao gồm quy tắc dựa trên thông tin trong tổng hợp khác?

No.
Trừ khi tổng hợp thứ hai được cung cấp qua đối số lệnh, tất nhiên.

CẢNH BÁO! ! !

Tôi đã một thực thể gọi là tài sản (thiết bị) ...
... (và a) tổng hợp thứ hai gọi là AssetType ...

Lần cuối cùng mà tôi đã phải đối phó với một cấu trúc tương tự, đó là một cơn đau.

Rất có thể bạn đang chọn trừu tượng sai.

tôi đã gặp lỗi bất biến của mình chưa?

Có lẽ ... Bạn đã yêu cầu chuyên gia tên miền ? Có phải anh ta nói về "TagTypes" không?

You should never abstract on your own.

Đối tượng thuộc loại X giữ tham chiếu đến phiên bản X-Type hầu như luôn có mùi trừu tượng, với hy vọng tái sử dụng, làm cho mô hình cứng nhắc và không linh hoạt trong quá trình phát triển kinh doanh.

ĐÁP

Nếu (và chỉ nếu) chuyên gia miền thực sự mô tả mô hình trong các điều khoản, một cách tiếp cận có thể là như sau:

  1. bạn có thể tạo một lớp AssetType với phương pháp nhà máy biến một số điện thoại IEnumerable<Tag> thành TagSetand throws hoặc MissingMandatoryTagException hoặc UnexpectedTagException nếu một số thẻ bị thiếu hoặc không mong muốn.
  2. trong lớp Asset, một lệnh RegisterTags sẽ chấp nhận một AssetType và một IEnumerable<Tag>, ném MissingMandatoryTagExceptionWrongAssetTypeException (lưu ý tầm quan trọng đang ngoại lệ để đảm bảo bất biến).

chỉnh sửa
một cái gì đó như thế này, nhưng nhiều hơn nữa tài liệu:

public class AssetType 
{ 
    private readonly Dictionary<TagType, bool> _tagTypes = new Dictionary<TagType, bool>(); 
    public AssetType(AssetTypeName name) 
    { 
     // validation here... 
     Name = name; 
    } 

    /// <summary> 
    /// Enable a tag type to be assigned to asset of this type. 
    /// </summary> 
    /// <param name="type"></param> 
    public void EnableTagType(TagType type) 
    { 
     // validation here... 
     _tagTypes[type] = false; 
    } 

    /// <summary> 
    /// Requires that a tag type is defined for any asset of this type. 
    /// </summary> 
    /// <param name="type"></param> 
    public void RequireTagType(TagType type) 
    { 
     // validation here... 
     _tagTypes[type] = false; 
    } 

    public AssetTypeName Name { get; private set; } 


    /// <summary> 
    /// Builds the tag set. 
    /// </summary> 
    /// <param name="tags">The tags.</param> 
    /// <returns>A set of tags for the current asset type.</returns> 
    /// <exception cref="ArgumentNullException"><paramref name="tags"/> is <c>null</c> or empty.</exception> 
    /// <exception cref="MissingMandatoryTagException">At least one of tags required 
    /// by the current asset type is missing in <paramref name="tags"/>.</exception> 
    /// <exception cref="UnexpectedTagException">At least one of the <paramref name="tags"/> 
    /// is not allowed for the current asset type.</exception> 
    /// <seealso cref="RequireTagType"/> 
    public TagSet BuildTagSet(IEnumerable<Tag> tags) 
    { 
     if (null == tags || tags.Count() == 0) 
      throw new ArgumentNullException("tags"); 
     TagSet tagSet = new TagSet(); 

     foreach (Tag tag in tags) 
     { 
      if(!_tagTypes.ContainsKey(tag.Key)) 
      { 
       string message = string.Format("Cannot use tag {0} in asset type {1}.", tag.Key, Name); 
       throw new UnexpectedTagException("tags", tag.Key, message); 
      } 
      tagSet.Add(tag); 
     } 

     foreach (TagType tagType in _tagTypes.Where(kvp => kvp.Value == true).Select(kvp => kvp.Key)) 
     { 
      if(!tagSet.Any(t => t.Key.Equals(tagType))) 
      { 
       string message = string.Format("You must provide the tag {0} to asset of type {1}.", tagType, Name); 
       throw new MissingMandatoryTagException("tags", tagType, message); 
      } 
     } 

     return tagSet; 
    } 
} 

public class Asset 
{ 
    public Asset(AssetName name, AssetTypeName type) 
    { 
     // validation here... 
     Name = name; 
     Type = type; 
    } 

    public TagSet Tags { get; private set; } 

    public AssetName Name { get; private set; } 

    public AssetTypeName Type { get; private set; } 

    /// <summary> 
    /// Registers the tags. 
    /// </summary> 
    /// <param name="tagType">Type of the tag.</param> 
    /// <param name="tags">The tags.</param> 
    /// <exception cref="ArgumentNullException"><paramref name="tagType"/> is <c>null</c> or 
    /// <paramref name="tags"/> is either <c>null</c> or empty.</exception> 
    /// <exception cref="WrongAssetTypeException"><paramref name="tagType"/> does not match 
    /// the <see cref="Type"/> of the current asset.</exception> 
    /// <exception cref="MissingMandatoryTagException">At least one of tags required 
    /// by the current asset type is missing in <paramref name="tags"/>.</exception> 
    /// <exception cref="UnexpectedTagException">At least one of the <paramref name="tags"/> 
    /// is not allowed for the current asset type.</exception> 
    public void RegisterTags(AssetType tagType, IEnumerable<Tag> tags) 
    { 
     if (null == tagType) throw new ArgumentNullException("tagType"); 
     if (!tagType.Name.Equals(Type)) 
     { 
      string message = string.Format("The asset {0} has type {1}, thus it can not handle tags defined for assets of type {2}.", Name, Type, tagType.Name); 
      throw new WrongAssetTypeException("tagType", tagType, message); 
     } 
     Tags = tagType.BuildTagSet(tags); 
    } 
} 
+1

Nghi ngờ của bạn rằng sự cố trong mô hình là chính xác. Chúng tôi đã dành một số thời gian (rất nhiều thực tế) để tìm ra chính xác những gì đang thực sự xảy ra. Tôi nghĩ rằng giải pháp "nhất quán cuối cùng" là tốt nhất. Khi họ quyết định họ muốn liệt kê một mẩu thông tin mới trên một Loại họ không thực sự cung cấp thông tin tại chỗ cho tất cả các tài sản. Vì vậy, một phương pháp "RebuildTags" trên Asset sẽ đọc AssetType và sau đó vô hiệu hóa trên cơ sở tài sản theo yêu cầu (và cũng có thể là "DetectTagErrors") Câu trả lời của bạn là tuyệt vời và đã nêu bật điều này mặc dù @eulerfx có câu trả lời thực tế. – MJM

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