The problem
A famous quote says: "There are only two hard things in Computer Science: cache invalidation and naming things"
We all know how important ubiquitous language / good names in DDD are.
I often find myself stuck thinking extremely hard about a good name for a new domain term. With experience I acquired instinct to try my best and come up with ideal name from the very first line of code, which is not always possible unfortunately.
But why? Why don't I just give it a good-enough name and rename it later? The reason is that domain terms tend to metastasize to all corners of code base while existing tools for renaming are shockingly primitive.
For the sake of example let's assume I want to rename term "InvoiceEntry" to more generic "DocumentEntry" (it can be anything else) because I found out that related code is reusable for different types of documents not just invoices.
Clarification: I explicitly use wording "domain term" rather than "domain entity" or other because there are other entities, classes, methods etc. (in a nutshell - identifers) that are derived from the same term. I don't even talk here about possible typos, let alone related database schema, string resources for UI etc.
If you are lucky, upon renaming of a type a smart IDE will suggest to rename some instances of the type, derived classes/implementations (if any), and will search for term in comments and dynamic invocations in absolutely context-insensitive manner i.e. just ambiguous suggestions not sure things. And that's it.
But the job is not over. At this stage I also must try "Find symbol" and then few iterations of good old "Find in Files" (including search with fuzzy free text chunks of the term that could be related e.g. "invoice entry", "invoice", "entry" etc.) which can yield hundreds of potentially related occurrences. All I can rely on is my attention, diligence and good judgement to make sure nothing related is missed and nothing irrelevant is affected. IDE simply can't resolve this ambiguity.
Conclusion: even if with best available IDE it is surprisingly laborious task to make a clean rename of domain term in a mature project. Doing it is a torture, reviewing someone else's renames is even worse.
It is easy to do the sloppy rename with a good IDE. However sloppy rename is arguably worse than no rename at all, otherwise your code can quickly turn into a mess (see "Broken windows theory")
Question and ideas of solution
If naming is so important then why renaming tools are still so poor? Can we do better than this?
Disclaimer: I will talk about strongly-typed languages as I don't use dynamic languages too much.
It's worth mentioning strongly typed functional languages with good type inference, they are a bit better at this however still far from ideal (and I can't just switch an old project to a functional language anyway):
Haskell has a tradition of giving abstract names to identifiers such as a, b, c, f etc. This is unthinkable in mainstream OO languages but not necessarily a bad thing. After all, if there is nothing else to tell about identifier that this is the instance of the type then it might be OK to name them "a" and "b" or "this" and "other" or alike. Abstract identifiers 100% immune to renames ("InvoiceEntry a" renamed to "DocumentEntry a" still makes sense, "DocumentEntry invoiceEntry1" doesn't)
with strong type system and type inference it is encouraged to create generic functions instead of specific types. More generic functions equals fewer mentions of specific domain term equals less code affected by renaming of a domain term.
Strongly-typed domain term metadata (an idea)
What I'm looking for is some kind unambiguous metadata mechanism to describe relations between identifiers / code elements and domain terms in ubiquitous language - and be able to traverse those relations to do something useful, specifically fast renaming of a domain term and everything that is derived from it.
I have an idea how I would do this in C#. Here's pseudocode (by no means valid or clean, but should make a concept clear):
// domain terms
public static class DomainBloodlineTerms
{
public static const string InvoiceEntry = "InvoiceEntry";
public static const string Repository = "Repository";
public static const string Id = "Id";
...
using _ = Domain.Static.DomainBloodlineTerms;
...
// a custom attribute to explicitly define relations
// between the identifier (class name in this case) and domain terms
[DomainBloodline(_.InvoiceEntry, _.Repository)
public class InvoiceEntryRepository
{
...
// a method with the bloodline
[DomainBloodline("Get", _.InvoiceEntry, "By", _.Id)]
public GetInvoiceEntryById(
// a parameter with the bloodline
[DomainBloodline(_.Id)] int id)
{
// a local variable with the bloodline
// (probably an overkill, but imperative style is possible too)
DomainBloodline.Verify(_.InvoiceEntry, "Dto")
var invoiceEntryDto = ...
I then can implement a couple of custom Code Analysis rules
- enforce that every identifier in namespace (or in project/solution) is annotated with the Bloodline attribute.
- enforce that all annotated identifiers are in sync with its "Bloodline" attribute
and custom refactoring:
- if member of DomainBloodlineTerms gets renamed then related identifiers will be renamed too.
with both rules above I can rest assured that everything related gets renamed. e.g. DomainBloodlineTerms.InvoiceEntry renamed to DocumentEntry - and related class, method, parameter and even variable will be renamed too.
Other benefits:
- disambiguation of identical terms from different sub-domains
- protection against typos
Other suggestions?
I don't like the "bloodline attribute" idea for few reasons:
- it is tedious and verbose
- "Find symbol" can already facilitate some parts of it (except checking for typos)
- it doesn't help to pinpoint related artifacts outside the code (SQL schema scripts, string resources etc.)
- I have a hunch that I'm reinventing the wheel
Do you know any existing solutions or better ideas to achieve the same result? .NET or any other language/ecosystem is good as a source of inspiration.