Back to snippets
Pattern

CDK Config-to-Model Pattern with Mustache

2 min read
awscdkjavamustacheinfrastructurepatterns

Environment-aware YAML configs with Mustache templating, deserialized to strongly-typed Java POJOs for CDK constructs.

In cdk-common, infrastructure config lives in environment-specific directories as Mustache templates. CDK context gets injected at synthesis, then YAML deserializes to typed POJOs.

resources/dev/v1/sqs/orders.mustache
name: {{hosted:id}}-orders
fifo: true
contentBasedDeduplication: true
visibilityTimeout: 30
retentionDays: 14
maxReceiveCount: 3
dlq:
  name: {{hosted:id}}-orders-dlq
  retentionDays: 14
encryption: KMS
kmsKeyArn: {{kms:orders}}
tags:
  service: orders
  environment: {{host:environment}}
yaml
SqsConf.java
@Value.Immutable @JsonDeserialize(as = ImmutableSqsConf.class)
public interface SqsConf {
  String name();
  @Value.Default default boolean fifo() { return false; }
  @Value.Default default int visibilityTimeout() { return 30; }
  @Value.Default default int retentionDays() { return 4; }
  Optional<Integer> maxReceiveCount();
  Optional<DlqConf> dlq();
  @Value.Default default String encryption() { return "KMS"; }
  Optional<String> kmsKeyArn();
  Map<String, String> tags();
}
java
SqsNestedStack.java
// Template.parse: reads file, compiles mustache, injects CDK context
var yaml = Template.parse(this, conf.sqs());
var sqsConf = Mapper.get().readValue(yaml, SqsConf.class);

var dlq = sqsConf.dlq().map(d -> DeadLetterQueue.builder()
    .queue(Queue.Builder.create(this, d.name())/*...*/.build())
    .maxReceiveCount(sqsConf.maxReceiveCount().orElse(3))
    .build());

Queue.Builder.create(this, sqsConf.name())
    .queueName(sqsConf.name() + ".fifo")
    .fifo(sqsConf.fifo())
    .contentBasedDeduplication(sqsConf.fifo())
    .visibilityTimeout(Duration.seconds(sqsConf.visibilityTimeout()))
    .retentionPeriod(Duration.days(sqsConf.retentionDays()))
    .deadLetterQueue(dlq.orElse(null))
    .encryptionMasterKey(Key.fromKeyArn(this, "Key", sqsConf.kmsKeyArn().orElseThrow()))
    .build();
java
Flow
resources/{env}/v1/{service}/*.mustache → Template.parse(scope, path) → Mapper.readValue(yaml, Conf.class) → CDK construct. Context like {{hosted:id}} and {{kms:orders}} resolves from cdk.json or runtime lookups.
By Brandon StokesWritten byClaude