One of the largest benefits that an application developer can get out of a cloud-based infrastructure is the opportunity to design for variable scale. Specifically, you can start off small (with a limited number of virtual machines, using limited host resources), and then expand your environment as usage grows. Conversely, you are able to shrink your infrastructure consumption during non-peak times. While some of this flexibility can be applied to existing legacy applications, the real win can be for newly developed systems.
To get this benefit, there are some fundamental architectural principals that need to be followed: loose coupling of system components, distributed system design and automated application installation / configuration. A solid architecture should reasonably scale from fitting the entire application onto a single VM to sharing it among hundreds (or even thousands) of VMs supporting the users.
To achieve the loose coupling and distributed design goals, you need to decompose the architecture into units of functionality and think through how they will distribute work within the system. Each component should be designed to support multiple instances of that application service within the environment. By doing this, you can load balance the application load as you need to scale.
This decomposition should happen at all layers. It does no good to scale out web servers if a singular application server will become the performance bottleneck. And definitely be sure to think through a scaling strategy for your databases. If you plan on using a traditional relational database platform (RDBMS), consider setting up your identity columns in a way that will support future distribution of load through sharding techniques. Another alternative is to use multiple read replicas, with a single write-enabled database instance. If you plan on going the route of NoSQL, be sure that you understand the scaling dynamics of the selected platform.
Achieving automated application installation and configuration builds on your distributed design. The key to ensuring that you can do achieve this architectural goal is to classify virtual machines into roles. Role definitions will let you relate one server to the other servers in the environment. Using a “web server” role as an example, perhaps any server in that role needs to know what database server to connect to. And just to relate this idea back to the point about determining a database scaling design, that database target might be different for different web servers.
Once you have a good understanding of how you plan to deploy a highly-distributed version of your system, it's time to automate your installation and configuration. These are critical tasks if you want to achieve value from a dynamic infrastructure environment, because you need to match the speed that you can install and configure an application with the speed that you can provision new infrastructure. Your software should be installable via command line, and you should look at different options to automate the configuration of the installed applications.
While you may want to take these concepts to the extreme, my best advice for a new application architecture is to start simple. Let these ideas guide your design, but remember, you’re main goal is to get the new application deployed for your users!