Modifying Constructs with Aspects
Relevant source files
The following files were used as context for generating this wiki page:Aspects are a powerful mechanism within the AWS CDK that allow for applying an operation or modification to all constructs within a specific scope. This pattern, inspired by Aspect-Oriented Programming (AOP), enables developers to implement cross-cutting concerns, such as enforcing tagging policies, applying security configurations, or performing custom validations, without modifying the individual constructs directly. An Aspect is applied to a construct, and it will then "visit" that construct and all of its children in the construct tree.
The core of this feature is the IAspect interface, which defines a visit method. Any class that implements this interface can be used as an aspect. The most common and illustrative use case for aspects is managing resource tags throughout a CDK application, as demonstrated by the Tag and RemoveTag aspects.
Sources: packages/@aws-cdk/core/lib/aspect.ts, design/tagging-API-change.md
Core Concepts
The Aspects framework is built around a few key components that work together during the CDK synthesis process.
The IAspect Interface
The central piece of the Aspects system is the IAspect interface. It defines a contract for any operation that needs to be applied across the construct tree.
// packages/@aws-cdk/core/lib/aspect.ts:5
export interface IAspect {
/**
* All aspects can visit an construct.
*/
visit(node: IConstruct): void;
}
Sources: packages/@aws-cdk/core/lib/aspect.ts:5-10
Any class implementing this interface must provide a visit method, which contains the logic to be executed on each construct (IConstruct) it encounters during the tree traversal.
Applying an Aspect
Aspects are applied to a scope using the applyAspect method on a construct's node property. When an aspect is applied to a construct, it will be invoked for that construct and every construct within its scope (i.e., all of its descendants in the construct tree).
// Example from design/tagging-API-change.md
myConstruct.node.applyAspect(new Tag('key', 'value'));
Sources: design/tagging-API-change.md
This application process adds the aspect to a list maintained by the construct. The actual visitation occurs during the "prepare" phase of the CDK synthesis lifecycle.
The Visitation Process
When the CDK application is synthesized, the framework traverses the construct tree and invokes the visit method of all registered aspects for each construct.
The following diagram illustrates the sequence of applying and visiting an aspect.
This diagram shows how a user applies an aspect, which is then invoked by the CDK framework during the synthesis/prepare phase for each construct in the scope.
The traversal is performed in a post-order fashion, meaning a construct's children are visited before the construct itself. This is confirmed by unit tests.
Sources: packages/@aws-cdk/core/test/aspect.test.ts:32-51
Concrete Implementation: Tagging
The most prominent use of Aspects in the CDK is for managing resource tags. The @aws-cdk/core library provides Tag and RemoveTag classes that implement IAspect.
Tag and RemoveTag Aspects
The Tag aspect adds a key-value pair to all taggable resources within a scope, while RemoveTag removes a tag by its key.
The visit method in these classes checks if a construct is taggable (ITaggable) and then uses its tagManager to perform the operation.
This class diagram shows the relationship between the core interfaces and the tagging implementation.
This diagram illustrates that Tag and RemoveTag are concrete implementations of the IAspect interface.
Simplified Tagging API
To make the API more intuitive, the Tag class provides static helper methods, add and remove. These methods abstract away the need to directly instantiate the aspect and call applyAspect.
// packages/@aws-cdk/core/lib/tag-aspect.ts:101-107
/**
* add tags to the node of a construct and all its the taggable children
*/
public static add(scope: Construct, key: string, value: string, props: TagProps = {}) {
scope.node.applyAspect(new Tag(key, value, props));
}
This allows for a more readable and declarative way to manage tags:
// Instead of this:
myConstruct.node.applyAspect(new Tag('foo', 'bar'));
// Users can do this:
Tag.add(myConstruct, 'foo', 'bar');
Sources: design/tagging-API-change.md, packages/@aws-cdk/core/lib/tag-aspect.ts:101-114
Tagging Properties
The tagging aspects can be configured with a TagProps object to control their behavior.
| Property | Type | Description |
|---|---|---|
applyToLaunchedInstance | boolean | Whether to apply the tag to instances launched by an Auto Scaling Group. (Default: true) |
includeResourceTypes | string[] | An array of resource types to apply the tag to. An empty array means all taggable resources. |
excludeResourceTypes | string[] | An array of resource types to exclude from tagging. |
priority | number | The priority of the tag operation. Higher numbers are applied later. (Default: 100 for Tag, 200 for RemoveTag) |
Sources: packages/@aws-cdk/core/lib/tag-aspect.ts:13-37
Advanced Usage and Safeguards
The Aspects framework includes safeguards to prevent common issues like infinite loops.
Applying Aspects During Visitation
An aspect can apply another aspect during its visit method. However, to prevent an infinite loop, an aspect cannot apply another aspect to the same node it is currently visiting. This is enforced by the framework and verified by unit tests.
// packages/@aws-cdk/core/test/aspect.test.ts:65-71
// THEN
expect(() => {
app.synth();
}).toThrow(/aspects cannot be applied from within a visit to the same node/);
This safeguard ensures that the visitation process terminates. An aspect is, however, allowed to apply another aspect to a different node within the tree during its visit.
Sources: packages/@aws-k/core/test/aspect.test.ts:53-72
Idempotency
The framework ensures that even if the same aspect instance is applied multiple times to the same scope, it is only visited once per construct. This prevents redundant operations.
Sources: packages/@aws-cdk/core/test/aspect.test.ts:74-90
Summary
Aspects provide a robust and flexible way to manage cross-cutting concerns in an AWS CDK application. By defining operations in IAspect implementations, developers can apply logic across entire scopes of their infrastructure, ensuring consistency and enforcing policies like tagging. The design includes important safeguards against infinite loops and redundant applications, making it a safe and powerful tool for advanced CDK users. The primary built-in use case is for tagging, which serves as a clear template for creating other custom aspects.
Sources: packages/@aws-cdk/core/lib/aspect.ts, design/tagging-API-change.md