DDD ubiquitous language check

Rule ND2018 Naming Conventions

Identifiers in the core domain should use the Domain Model language — the ubiquitous language of Domain Driven Design (DDD): naming rigor, since software doesn't cope well with ambiguity.

Disabled per default: customize the core domain namespace name and the domain terms list. Mention singular and plural forms both if needed (e.g. Seat and Seats); see also the related rule Properties and fields that represent a collection of items should be named Items.

Relies on the NDepend API extension method ExtensionMethodsString.GetWords(this string identifier) that smartly extracts terms from identifiers.

How to fix

The rule lists the words not in vocabulary per violation. Either rename the code element or add the missing term(s) to the vocabulary list in the rule source.

CQLinq source

This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.

// <Name>DDD ubiquitous language check</Name>
// <Id>ND2018:DDDUbiquitousLanguageCheck</Id>

warnif count > 0

// Update to your core domain namespace(s)
let coreDomainNamespaces = Application.Namespaces.WithNameLike("TrainTrain.Domain")
 
// Update your vocabulary list
let vocabulary = new [] {
  "Train", "Coach", "Coaches", "Seat", "Seats",
  "Reservation", "Fulfilled", "Booking", "Book", "Reserve", "Confirm"
}.ToHashSetEx()
 
let technicalWords = new [] { "get", "set", "Get", "Set", "Add" }.ToHashSetEx()
let multiWordsVocabulary = vocabulary.Where(w => w.GetWords().Length >= 2)
 
// Append multi-words words in vocabulary
let multiWordsWords = multiWordsVocabulary.SelectMany(w => w.GetWords())
let vocabulary2 = vocabulary.Concat(multiWordsWords).ToHashSetEx()
let vocabulary3 = vocabulary2.Concat(technicalWords).ToHashSetEx()
 
from ce in coreDomainNamespaces.ChildTypesAndMembers()
let tokens = ce.SimpleName.GetWords().Select(w => w.FirstCharToUpper()).ToArray()
 
where !(ce.IsMethod && ce.AsMethod.IsConstructor) && // No vocabulary in ctor
      !(ce.IsField && ce.AsField.IsGeneratedByCompiler) &&  // Remove compiler generated backing fields, their vocabulary is in property
      !(vocabulary3.Any(vocable => tokens.Contains(vocable)))
 
let wordsNotInVocabulary = tokens.Where(w => !(vocabulary2.Contains(w) && string.IsNullOrEmpty(w))).ToArray()
 
select new { ce,
  wordsNotInVocabulary = string.Join(", ",wordsNotInVocabulary)
}

//<Expl>
// The *{0}'s name* contains words like *{1}* which are not part 
// of the domain vocabulary defined in this rule source code.
//</Expl>

//<Desc>
// Identifiers in the **core domain** should use the **Domain Model**
// language — the **ubiquitous language** of **Domain Driven Design (DDD)**:
// naming rigor, since software doesn't cope well with ambiguity.
//
// Disabled per default: customize the **core domain** namespace name and
// the domain terms list. Mention singular and plural forms both if needed
// (e.g. **Seat** and **Seats**); see also the related rule *Properties and
// fields that represent a collection of items should be named Items*.
//
// Relies on the NDepend API extension method
// **ExtensionMethodsString.GetWords(this string identifier)** that smartly
// extracts terms from identifiers.
//</Desc>

//<HowToFix>
// The rule lists the **words not in vocabulary** per violation. Either
// rename the code element or add the missing term(s) to the vocabulary list
// in the rule source.
//</HowToFix>

Naming Conventions: all 23 items