IDP Pattern: Components using other Components
Description
This pattern involves creating Pulumi components that internally use other components, creating a hierarchy of reusable infrastructure patterns. Higher-level components encapsulate complex architectures while lower-level components handle specific infrastructure concerns.
When to use this pattern
- Integrated architectures: When you need to combine multiple types of infrastructure
- Shared components: When you have components that need to be used both internally by higher-level components and directly by end users
- Reusable patterns: When you have common architectural patterns across applications
- Progressive complexity: When you want to build from simple to complex components
When NOT to use this pattern
- Simple use cases: When a single component would suffice
- Over-abstraction: When the complexity doesn’t align with your organization’s developer experience or policy needs
How to use this pattern
Components can compose other components to build higher-level abstractions while maintaining clear interfaces and responsibilities.
Example
Building a web application component that uses database and networking components:
// Lower-level components
export class Database extends ComponentResource {
public readonly connectionString: Output<string>;
public readonly securityGroupId: Output<string>;
constructor(name: string, args: DatabaseArgs, opts?: ComponentResourceOptions) {
super("acme:components:Database", name, {}, opts);
// Create security group for database access
const dbSecurityGroup = new aws.ec2.SecurityGroup(`${name}-sg`, {
vpcId: args.vpcId,
ingress: [{
protocol: "tcp",
fromPort: 5432,
toPort: 5432,
cidrBlocks: [args.allowedCidr],
}],
});
// Create DB subnet group
const subnetGroup = new aws.rds.SubnetGroup(`${name}-subnet-group`, {
subnetIds: args.subnetIds,
});
// Create RDS instance
const db = new aws.rds.Instance(name, {
engine: "postgres",
instanceClass: args.instanceClass,
allocatedStorage: args.storage,
dbSubnetGroupName: subnetGroup.name,
vpcSecurityGroupIds: [dbSecurityGroup.id],
backupRetentionPeriod: args.backupDays || 7,
// ... other configuration
});
this.connectionString = db.endpoint.apply(endpoint =>
`postgresql://${args.username}:${args.password}@${endpoint}/${args.dbName}`
);
this.securityGroupId = dbSecurityGroup.id;
}
}
export class Network extends ComponentResource {
public readonly vpcId: Output<string>;
public readonly subnetIds: Output<string[]>;
constructor(name: string, args: NetworkArgs, opts?: ComponentResourceOptions) {
super("acme:components:Network", name, {}, opts);
const vpc = new aws.ec2.Vpc(name, {
cidrBlock: args.cidrBlock,
});
// Create subnets, security groups, etc.
this.vpcId = vpc.id;
this.subnetIds = subnets.map(s => s.id);
}
}
// Higher-level component using other components
export class WebApplication extends ComponentResource {
constructor(name: string, args: WebApplicationArgs, opts?: ComponentResourceOptions) {
super("acme:patterns:WebApplication", name, {}, opts);
// Use network component
const network = new Network(`${name}-network`, {
cidrBlock: "10.0.0.0/16",
}, { parent: this });
// Use database component
const database = new Database(`${name}-db`, {
instanceClass: "db.t3.micro",
storage: 20,
username: args.dbUsername,
password: args.dbPassword,
dbName: args.dbName,
}, { parent: this });
// Application infrastructure using the components
const app = new aws.ecs.Service(`${name}-app`, {
// Use network.vpcId and database.connectionString
// ... application configuration
}, { parent: this });
}
}
Teams can use the high-level component without managing individual infrastructure pieces:
const myApp = new WebApplication("my-web-app", {
dbUsername: "admin",
dbPassword: dbPassword,
dbName: "myapp",
});
Related patterns
- IDP Pattern: Container-based apps, centrally managed container infra - Example of component composition
- IDP Pattern: Validating Component Inputs using Policy functions - For validating composed components
- IDP Pattern: Security Updates using Components - For updating component hierarchies
Thank you for your feedback!
If you have a question about how to use Pulumi, reach out in Community Slack.
Open an issue on GitHub to report a problem or suggest an improvement.