Reasons to consider decoupling when designing your AWS application:
- You might want to develop some code on AWS because you have quick access to servers, unlike the traditional large corporate environment where you put in a request and wait for weeks or months. It might be that you are still required to use the in-house corporate servers for production but you want to get started on development in the AWS environment. Decoupling your design allows for minimal changes and code that can switch from one environment to the other.
- Provide flexibility for the possibility (heaven forbid!) that someday you move off AWS to some other platform. You'll want to minimize the changes you have to make to your application should that become necessary for one or more of the AWS components you are using.
- On the flip side you might want to build and internal application that has the flexibility to scale to AWS servers when necessary and run with AWS IAM roles when in the cloud. You want your application to be dynamic enough to run in either location.
- Finally there is the best practice of writing unit tests which might need to run with or without AWS resources being available. By designing to interfaces or decoupling your application from AWS using proxy classes that can run with or without AWS you can run unit tests without access to AWS.
One way to decouple your design is to create a set of proxy services that get called and forward requests to AWS. This is more work and possibly more error prone due to network issues. It creates complexity that will make it harder to maintain the application and requires more resources, but can provide separation of duties in the case where you want some people writing your AWS code and other people writing the applications that use the AWS code but not accessing AWS components directly to maintain decoupled applications. This option is nice because no changes to your application code or recompilation is required should whatever is behind the proxy change.
Another option is to create a set of interfaces used by your code. You could have the AWS objects extend these interfaces so you can use polymorphism and some configuration file to determine which class to use at the time the program runs. The down side of this and why I don't really recommend it is because I prefer not to get in and tweak vendor files unless I'm going to completely take ownership of that code and maintain it going forward. Amazon is going to keep changing their libraries and I want to keep using those libraries.
Another option would be to create wrapper or proxy classes that interact with AWS components. Put all your proxy classes in a separate project and create a separate library that has a dependency on the AWS components. This is nice because none of your application directly calls AWS code and should not include any AWS libraries to prevent any developers from inadvertently creating dependencies.
As for configuration you have a few different options to tell your application which classes to use:
- Define your classes in configuration files similar to the way Spring defines the actual class to use and gives it a name so you can swap out classes behind the scenes. Have a configuration file for AWS and running without AWS where you use mock objects or something else.
- Define a switch in your config file which says "use AWS - yes or no" basically. Then create a factory that loads the appropriate object based on this switch - this would allow you to change your factory to extend your application or to easily swap out classes used should you introduce new vendors or functionality.