AWS CDK using Java and project directory structure

Jan Julien Nicolas
6 min readAug 18, 2021

Hi co-software engineers and AWS cloud enthusiasts!

This is my first tech blog and it’s about AWS CDK using Java and how we organize its project directory structure. We’re also a bit new to this topic but I am here to show you how we do organize our code, and maybe you guys will be able to pick-up some new ideas on how you’ll also organize your project directory structure.

I’ll be assuming that you already know how to create an initial CDK project by reading the official docs, as well as a thing or two with different AWS services already (S3 buckets and KMS Keys in our examples), and especially with CloudFormation — since our way to organize the structure is mostly based on its resource type naming convention. So let’s start!

When you initially create a CDK project with language configured for Java, it will look like this (I’m using VS Code by the way):

AWS CDK init project boilerplate code

As you can see in the auto-generated comment, code that defines your stack goes there at that section, then as a beginner, others would really put all their CDK code directly at those lines, and that would definitely look messy or spaghetti code.

In this guide, I will be creating an S3 bucket, and show a basic directory structure to where will I put it. That is, to create a resources folder, then under it an s3 folder, and then put the S3 bucket/s in there:

Buckets.java under resources/s3 folder (or package)

Yes, it’s plural — Buckets.java, because the idea is, any other bucket you would want to create, should go in there. Also mind the package naming convention, we put it inside resources, since it is technically a real AWS resource when deployed, then inside resources folder, another folder (or package) called s3, because a Bucket is an S3 resource.

So if we would want to create add another AWS resource, we will have to create another folder in the resources folder, for example, we want to create a KMS key we create it like this:

Keys.java under resources/kms folder (or package)

And the idea is, all KMS keys you plan to create will also go inside that Keys.java file. As with Buckets.java, we follow the package naming convention similar to how CloudFormation organizes their resources such that when you look at CloudFormation documentation, you will see that Buckets are of Type AWS::S3::Bucket, then KMS keys are of Type AWS::KMS::Key.

Bucket resource as seen from AWS CloudFormation documentation
KMS Key resource as seen from AWS CloudFormation documentation

So if you plan to create other resources, the idea is to look at CloudFormation docs and see the resource type’s naming convention — AWS::ECS::TaskDefinition, will go to *.resources.ecs.Tasks package, AWS::ElasticLoadBalancingV2::LoadBalancer, goes to the *.resources.elasticloadbalancing.LoadBalancers package, and so on. You get the point.

Now that we have a basic directory structure, how does those files look like? Well, here it is:

Buckets.java

Some brief explanation:

  • The constructor takes in the Construct object and is initialized with the private field scope. This is because the scope is needed whenever we create a new CDK construct, and in this case, any bucket construct we define will need to know which Stack scope it belongs to.
  • Methods are public and are to be called in your *Stack.java code (which will be shown later). They also return the type of resource you are creating, this is important to define because of the resource dependency within your CDK stack code — which will be discussed later.
  • When calling the createJuliennTechBucket method, the juliennTechBucket being created should belong in the scope of the Stack being passed from the Buckets.java constructor. In our case, this would be the AwsCdkJavaProjectStructureStack.java which is a Stack construct.
  • And when you need to create other buckets, you create another method inside this Buckets class.

Now that the Buckets.java source code is complete, this is how we’ll use it to in the stack code to make it synthesizable:

AwsCdkJavaProjectStructureStack.java

Again, some explanation:

  • We define buckets as a private field, and instantiate it in the constructor AwsCdkJavaProjectStructureStack(Construct, String, StackProps) at line 20. The reason behind is for testability purposes, which I will talk about in a future tech blog.
  • A separate void method createAwsCdkJavaProjectStructureStack() is created, and that is where resource creation methods will be called such as the createJuliennTechBucket() method created earlier. The reason is similar to the first — making it testable.

Now, the code is complete and executing cdk synth should work without any issues:

Executing cdk synth command

Since it has synthesized successfully, it should now be possible to deploy to your AWS environment using cdk deploy command.

The next part I would also like to share is what I mentioned earlier about the resource dependencies. Like, how do we define the dependency of one AWS resource to another AWS resource here in CDK with the project directory structure I shared?

Let me give you an example, in our case, I already created the Keys.java above, so let’s try to make our S3 bucket be encrypted with a custom KMS key created from Keys.java and make the S3 bucket dependent with that KMS key, and here is the Keys.java code:

Keys.java

It should follow the same explanation I mentioned with Buckets.java above, and I will assume you’d already know how KMS keys can be defined with AWS CDK.

Then we’ll need to make a few changes in Buckets.java specifically in createJuliennTechBucket() method to accommodate for that new KMS key we had just defined:

Modified Buckets.java with encryption key in the createJuliennTechBucket(Key) method

Yes, we added the props encryption to specify it will be using KMS bucket encryption and props encryptionKey to specify the specific KMS key the bucket will be using. In addition to that, the method now accepts a key parameter of Key object from *.kms.Key package of CDK, and this is where we’ll pass the key we created earlier.

Then combining them all up into the AwsCdkJavaProjectStructureStack.java:

Modifying AwsCdkJavaProjectStructureStack.java to accommodate new KMS Key dependency of S3 bucket

Some brief explanation:

  • Similar to what we did earlier, Keys are defined as private fields, and instantiated in the constructor for reasons of making it a testable code.
  • Around lines 30–31, this is where we exactly define the resource dependency, it is also the reason why the resource creation methods of each resource classes (Buckets.java, Keys.java) return the type of resource they create — so we can pass those resources around other resources that may need them as a dependency. In our case, createJuliennTechBucket method needs the juliennTechKey in order to create the S3 bucket with KMS encryption we need.

With that, our code is once again completed with the resource dependency in place, and we can now execute cdk synth successfully:

cdk synth command execution with KMS key dependency

And you can clearly see how CDK was able to properly generate the CloudFormation template with the KMS key as the dependency of the S3 bucket for encryption.

So that’s it for this tech blog/ guide on AWS CDK using Java and project directory structure, where I’ve shared not only our directory structure, but also how we did our package naming convention. In addition to that, I’ve also shown you how to combine it all, as well as define the dependencies properly in Stack code and execute cdk synth command with it.

Please do follow if you enjoyed or learned something new in this blog, and in the next blog post, I will be writing about how do we do our testing with AWS CDK using Java, and using the same example codes above.

Thanks for reading, and until next time!

--

--

Jan Julien Nicolas

A Senior Engineer at Trend Micro PH passionate about software engineering and AWS. Likes to try out a lot of things, but mostly loves to play with 2yo daughter.