Open sandboxFocusImprove this doc

Enforcing naming conventions

In any professional team, consistent terminology usage is essential. This is especially true for large codebases maintained by multiple people over extended periods.

To ensure consistency, Metalama provides a straightforward way to enforce naming conventions across lines of inheritance. For instance, you can require any class inheriting the IFactory interface to have a name ending with the Factory prefix. This ensures everyone uses the same language and understands the same concepts.

Enforcing naming conventions using custom attributes

If you want to enforce a naming convention for any type that derives from a given class or interface, and you own the source code of this class or interface, the easiest approach is to use a custom attribute. Follow these steps:

  1. Add the Metalama.Extensions.Architecture package to your project.

  2. Apply the DerivedTypesMustRespectNamingConventionAttribute custom attribute to the base class or interface. The argument of this custom attribute must be the naming pattern, where the asterisk (*) matches any substring.

Note

If you want full control over the regular expression, use DerivedTypesMustRespectRegexNamingConventionAttribute.

Example

In the following example, we require all types implementing IFactory to have a name ending with the Factory suffix.

1using Metalama.Extensions.Architecture.Aspects;
2
3namespace Doc.Architecture.NamingConvention;
4
5[DerivedTypesMustRespectNamingConvention( "*Factory" )]
6public interface IFactory { }
7
8// This will report a warning because the naming convention is not respected.
Warning LAMA0903: The type 'ThingCreator' does not respect the naming convention set on the base class or interface 'IFactory'. The type name should match the "*Factory" pattern.

9internal class ThingCreator : IFactory { }
10
11// This is properly named.
12internal class WidgetFactory : IFactory { }

Enforcing naming conventions using fabrics

If you want to enforce naming conventions for a scenario different from the one above, you cannot use custom attributes. Instead, you need to use fabrics and write compile-time code. Follow these steps:

  1. Add the Metalama.Extensions.Architecture package to your project.

  2. Create or reuse a fabric type as described in Fabrics.

  3. Import the Metalama.Extensions.Architecture.Fabrics namespace to benefit from extension methods.

  4. Edit the AmendProject, AmendNamespace or AmendType method.

  5. Select the APIs using the Select, SelectMany and Where methods. You may also find the SelectTypesDerivedFrom method useful.

  6. Call the MustRespectNamingConvention method.

Note

Unlike DerivedTypesMustRespectNamingConventionAttribute, the naming convention added by the MustRespectNamingConvention is neither inherited nor cross-project. If you want the naming convention to apply to several projects, use the techniques described in Adding aspects to multiple projects.

Example: Enforcing a naming convention on all types derived from a given system type

Many teams require UI pages to be suffixed with Page, controls with Control, and so on. This cannot be achieved using a custom attribute because you don't own the source code of the base class. In the following example, we show how to implement this requirement: we require all classes derived from TextReader to be suffixed with Reader. We use the SelectTypesDerivedFrom method to select the relevant types.

Source Code
1using Metalama.Extensions.Architecture;
2using Metalama.Framework.Fabrics;
3using System.IO;
4
5namespace Doc.Architecture.NamingConvention_Fabric;
6
7internal class Fabric : ProjectFabric
8{



9    public override void AmendProject( IProjectAmender amender )
10    {
11        amender.SelectTypesDerivedFrom( typeof(TextReader) )
12            .MustRespectNamingConvention( "*Reader" );
13    }
14}
15
16// The naming convention is broken.



Warning LAMA0906: The type 'TextLoader' does not respect the naming convention set by a fabric. The type name should match the "^.*Reader$" pattern.

17internal class TextLoader : TextReader { }
Transformed Code
1using Metalama.Extensions.Architecture;
2using Metalama.Framework.Fabrics;
3using System.IO;
4
5namespace Doc.Architecture.NamingConvention_Fabric;
6
7
8#pragma warning disable CS0067, CS8618, CS0162, CS0169, CS0414, CA1822, CA1823, IDE0051, IDE0052
9
10internal class Fabric : ProjectFabric
11{
12    public override void AmendProject(IProjectAmender amender) => throw new System.NotSupportedException("Compile-time-only code cannot be called at run-time.");
13}


14

15#pragma warning restore CS0067, CS8618, CS0162, CS0169, CS0414, CA1822, CA1823, IDE0051, IDE0052
16
17
18
19// The naming convention is broken.
20internal class TextLoader : TextReader { }