 <blockquote> <p>Can an aggregates invariant include a rule based on information from elsewhere?</p> </blockquote> <p>Aggregates can always use the informations in their own states and the argument that their <a href="http://epic.tesio.it/doc/manual/command_query_separation.html" rel="nofollow">commands</a> recieves.</p> <p>Someone use to access applicative services via singletons, service locators and so on, but IMO, that's a smell of tightly coupled applications. They forget that methods' arguments are effective dependency injectors! :-)</p> <blockquote> <p>In DDD can an aggregates invariant include a rule based on information in a another aggregate?</p> </blockquote> <p>No.<br> Except if the second aggregate is provided via commands' arguments, of course. </p> <p><strong>WARNING ! ! !</strong></p> <blockquote> <p>I have an entity called Asset (equipment)...<br> ... (and a) second aggregate called AssetType...</p> </blockquote> <p>The last time that I had to cope with a similar structure, it was a pain.</p> <p>Chances are that you are choosing the wrong abstractions.</p> <blockquote> <p>have I got my invariant wrong?</p> </blockquote> <p>Probably... Did you asked to the <strong>domain expert</strong>? Does <strong>he</strong> talks about "TagTypes"?</p> <p><a href="http://epic.tesio.it/doc/manual/solid_principles.html#about_liskov_substitution" rel="nofollow">You should never abstract on your own</a>.</p> <p>Entities of type <code>X</code> holding a reference to an instance of an <code>X-Type</code> are almost always a smell of over-abstraction, that in the hope of reuse, makes the model rigid and inflexible to business evolution.</p> <p><strong>ANSWER</strong></p> <p>If <em>(and only if)</em> the domain expert actually described the model in these terms, a possible approach is the following:</p> <ol> <li>you can create an <code>AssetType</code> class with a factory method that turns an <code>IEnumerable&lt;Tag&gt;</code> into a <code>TagSet</code> <a href="http://epic.tesio.it/2013/03/04/exceptions-are-terms-ot-the-ubiquitous-language.html" rel="nofollow">and throws</a> either <code>MissingMandatoryTagException</code> or <code>UnexpectedTagException</code> if some of the tag is missing or unexpected. </li> <li>in the <code>Asset</code> class, a command <code>RegisterTags</code> would accept an <code>AssetType</code> and an <code>IEnumerable&lt;Tag&gt;</code>, throwing the <code>MissingMandatoryTagException</code> and <code>WrongAssetTypeException</code> (note how important are exceptions to ensure invariants).</li> </ol> <p><strong>edit</strong><br> something like this, but much more documented:</p> <pre class="lang-cs prettyprint-override"><code>public class AssetType { private readonly Dictionary&lt;TagType, bool&gt; _tagTypes = new Dictionary&lt;TagType, bool&gt;(); public AssetType(AssetTypeName name) { // validation here... Name = name; } /// &lt;summary&gt; /// Enable a tag type to be assigned to asset of this type. /// &lt;/summary&gt; /// &lt;param name="type"&gt;&lt;/param&gt; public void EnableTagType(TagType type) { // validation here... _tagTypes[type] = false; } /// &lt;summary&gt; /// Requires that a tag type is defined for any asset of this type. /// &lt;/summary&gt; /// &lt;param name="type"&gt;&lt;/param&gt; public void RequireTagType(TagType type) { // validation here... _tagTypes[type] = false; } public AssetTypeName Name { get; private set; } /// &lt;summary&gt; /// Builds the tag set. /// &lt;/summary&gt; /// &lt;param name="tags"&gt;The tags.&lt;/param&gt; /// &lt;returns&gt;A set of tags for the current asset type.&lt;/returns&gt; /// &lt;exception cref="ArgumentNullException"&gt;&lt;paramref name="tags"/&gt; is &lt;c&gt;null&lt;/c&gt; or empty.&lt;/exception&gt; /// &lt;exception cref="MissingMandatoryTagException"&gt;At least one of tags required /// by the current asset type is missing in &lt;paramref name="tags"/&gt;.&lt;/exception&gt; /// &lt;exception cref="UnexpectedTagException"&gt;At least one of the &lt;paramref name="tags"/&gt; /// is not allowed for the current asset type.&lt;/exception&gt; /// &lt;seealso cref="RequireTagType"/&gt; public TagSet BuildTagSet(IEnumerable&lt;Tag&gt; 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 =&gt; kvp.Value == true).Select(kvp =&gt; kvp.Key)) { if(!tagSet.Any(t =&gt; 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; } /// &lt;summary&gt; /// Registers the tags. /// &lt;/summary&gt; /// &lt;param name="tagType"&gt;Type of the tag.&lt;/param&gt; /// &lt;param name="tags"&gt;The tags.&lt;/param&gt; /// &lt;exception cref="ArgumentNullException"&gt;&lt;paramref name="tagType"/&gt; is &lt;c&gt;null&lt;/c&gt; or /// &lt;paramref name="tags"/&gt; is either &lt;c&gt;null&lt;/c&gt; or empty.&lt;/exception&gt; /// &lt;exception cref="WrongAssetTypeException"&gt;&lt;paramref name="tagType"/&gt; does not match /// the &lt;see cref="Type"/&gt; of the current asset.&lt;/exception&gt; /// &lt;exception cref="MissingMandatoryTagException"&gt;At least one of tags required /// by the current asset type is missing in &lt;paramref name="tags"/&gt;.&lt;/exception&gt; /// &lt;exception cref="UnexpectedTagException"&gt;At least one of the &lt;paramref name="tags"/&gt; /// is not allowed for the current asset type.&lt;/exception&gt; public void RegisterTags(AssetType tagType, IEnumerable&lt;Tag&gt; 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); } } </code></pre>