Over the last few chapters we have looked at how to:

All this is available in a sample repo that you can deploy and test.

Now we can finally look at how to deploy our services. The addition of cross-stack references to our services means that we have some built-in dependencies. This means that we need to deploy some services before we deploy certain others.

Service Dependencies

Following is a list of the services we created:


And based on our cross-stack references the dependencies look roughly like:

database > notes > users

uploads > auth

Where the a > b symbolizes that service a needs to be deployed before service b. To break it down in detail:

  • The users API service relies on the notes API service for the API Gateway cross-stack reference.

  • The users and notes API services rely on the database service for the DynamoDB cross-stack reference.

  • And the auth service relies on the uploads and notes service for the S3 bucket and API Gateway cross-stack references respectively.

Hence to deploy all of our services we need to follow this order:

  1. database
  2. uploads
  3. notes
  4. users
  5. auth

Now there are some intricacies here but that is the general idea.

Multi-Service Deployments

Given the rough dependency graph above, you can script your CI/CD pipeline to ensure that your automatic deployments follow these rules. There are a few ways to simplify this process.

It is very likely that your auth, database, and uploads service don’t change very often. You might also need to follow some strict policies across your team to make sure no haphazard changes are made to it. So by separating out these resources into their own services (like we have done in the past few chapters) you can carry out updates to these services by using a manual approval step as a part of the deployment process. This leaves the API services. These need to be deployed manually once and can later be automated.

It should also be noted that while you won’t be able to automatically deploy using Seed as we talked about in Part II of this guide. We are working on a way to handle this automatically and feel free to get in touch with us if you are interested.


A quick word of handling environments across these services. The services that we have created can be easily re-created for multiple environments or stages. A good standard practice is to have a dev, staging, and prod environment. And it makes sense to replicate all your services across these three environments.

However, when you are working on a new feature or you want to give a developer on your team their own environment, it might not make sense to replicate all of your services across them. It is more common to only replicate the API services as you create multiple dev environments.

Mono-Repo vs Multi-Repo

Finally, when considering how to house these services in your repository, it is worth looking at how much code is shared across them. Typically, your infrastructure services (database, uploads and auth) don’t share any code between them. In fact they probably don’t have any code in them to begin with. These services can be put in their own repos. Whereas the API services that might share some code (request and response handling) can be placed in the same repo and follow the mono-repo approach outlined in the Organizing Serverless Projects chapter.

This combined way of using the multi-repo and mono-repo strategy also makes sense when you think about how we deploy them. As we stated above, the infrastructure services are probably going to be deployed manually and with caution. While the API services can be automated (using Seed or your own CI) for the mono-repo services and handle the others ones as a special case.


Hopefully these series of chapters have given you a sense of how to structure large Serverless applications using CloudFormation cross-stack references. And the example repo gives you a clear working demonstration of the concepts we’ve covered. Give the above setup a try and leave us your feedback in the comments.