|
| 1 | +# Get your development team to adhere to architecture |
| 2 | + |
| 3 | +Developers need to follow specific rules and conventions to work together effectively as a team. This adherence ensures that individual contributions integrate seamlessly into the overall application. |
| 4 | + |
| 5 | +In a previous email, we discussed how to enforce naming conventions. Today, let's examine how to verify that components are _used_ as expected. You might have just written a method meant only to support a test, but how can you enforce that it's not used in production code? |
| 6 | + |
| 7 | +* Option 1: Add comments such as **DO NOT USE IN PRODUCTION CODE!**, hoping the exclamation mark will help. |
| 8 | +* Option 2: Rely on code reviews. This can be slow, frustrating, and costly. |
| 9 | +* Option 3: Make your architecture _executable_ so it can be automatically enforced straight from the editor. You guessed it, that's where Metalama comes in handy. |
| 10 | + |
| 11 | +The [open-source](https://github.com/postsharp/Metalama.Extensions/tree/HEAD/src/Metalama.Extensions.Architecture) [Metalama.Extensions.Architecture](https://www.nuget.org/packages/Metalama.Extensions.Architecture) package offers several pre-made custom attributes and compile-time APIs that cover many common conventions teams might want to follow. |
| 12 | + |
| 13 | +## Custom attributes |
| 14 | + |
| 15 | +Let's assume we have a constructor that slightly modifies the object's behavior to make it more testable. We want to ensure that this constructor is used only in tests. Metalama provides the [CanOnlyBeUsedFrom](https://doc.postsharp.net/etalama/api/metalama-extensions-architecture-aspects-canonlybeusedfromattribute) attribute for this purpose. |
| 16 | + |
| 17 | +```c# |
| 18 | +using Metalama.Extensions.Architecture.Aspects; |
| 19 | + |
| 20 | +namespace CommonTasks.ValidatingArchitecture |
| 21 | +{ |
| 22 | + public class OrderShipping |
| 23 | + { |
| 24 | + private bool isTest; |
| 25 | + |
| 26 | + public OrderShipping() |
| 27 | + { |
| 28 | + } |
| 29 | + |
| 30 | + [CanOnlyBeUsedFrom(Namespaces = new[] {"**.Tests"})] |
| 31 | + public OrderShipping(bool isTest) |
| 32 | + { |
| 33 | + // Used to trigger specific test configuration |
| 34 | + this.isTest = isTest; |
| 35 | + } |
| 36 | + } |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +If we attempt to create a new `OrderShipping` instance in a namespace that isn't suffixed by `Tests`, we will see a warning. |
| 41 | + |
| 42 | + |
| 43 | + |
| 44 | +## Fabrics |
| 45 | + |
| 46 | +Suppose we have a project composed of a large number of components. Each of these components is implemented in its own namespace and is made up of several classes. There are so many components that we don't want to have them each in their own project. |
| 47 | + |
| 48 | +However, we still want to isolate components from each other. Specifically, we want `internal` members of each namespace to be visible only within this namespace. Only `public` members should be accessible outside of its home namespace. |
| 49 | + |
| 50 | +Additionally, we want `internal` components to be accessible from any test namespace. |
| 51 | + |
| 52 | +With Metalama, you can validate each namespace by adding a _fabric_ type: a compile-time class that executes within the compiler or the IDE. |
| 53 | + |
| 54 | +```cs |
| 55 | +namespace MyComponent |
| 56 | +{ |
| 57 | + internal class Fabric : NamespaceFabric |
| 58 | + { |
| 59 | + public override void AmendNamespace(INamespaceAmender amender) |
| 60 | + { |
| 61 | + amender.InternalsCanOnlyBeUsedFrom(from => |
| 62 | + from.CurrentNamespace().Or(or => or.Type("**.Tests.**"))); |
| 63 | + } |
| 64 | + } |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +Now, if some foreign code tries to access an internal API of the `MyComponent` namespace, a warning will be reported. |
| 69 | + |
| 70 | +In addition to `InternalsCanOnlyBeUsedFrom`, the package also includes `InternalsCannotBeUsedFrom`, `CanOnlyBeUsedFrom`, and `CannotOnlyBeUsedFrom`. You can easily build more rules based on the code model. |
| 71 | + |
| 72 | +## Conclusion |
| 73 | + |
| 74 | +We've just seen two examples of how you can validate your code using pre-built Metalama aspects or compile-time APIs. |
| 75 | + |
| 76 | +Enforcing rules and conventions in this manner allows you to: |
| 77 | + |
| 78 | +- Eliminate the need for a written set of rules to which everyone must refer. |
| 79 | +- Provide immediate feedback to developers within the familiar confines of the IDE itself. |
| 80 | +- Improve code reviews as they now only need to focus on the code itself. |
| 81 | +- Simplify the codebase because it adheres to consistent rules. |
| 82 | + |
| 83 | +You can learn more about architecture validation in our online [documentation](https://doc.postsharp.net/metalama/conceptual/architecture/usage). |
0 commit comments