In order to prevent spaghetti code in Puppet I am applying the Roles/Profiles pattern. Now I am learning Ansible and I am concerned whether spaghetti code will occur like back in the days when I started with Puppet. What patterns could be applied in Ansible to prevent code duplication and spaghetti code?
Ansible has the concepts of roles and dependencies which can be used in conjunction to create a roles/profiles/components pattern as you do in Puppet. You could design your role as a component, as a profile or as a role. Probably you would need a naming convention structure to not get confused about the difference between them. If you want to build a webserver role (puppet pattern) you could have 5 roles (ansible roles), component-apache, component-mysql, profile-database, profile-webserver, role-webserver. The first 2 will be roles that installs and configure a generic apache/mysql software (you should find it at ansible-galaxy). The 2 profiles will add these 2 components as dependencies (in meta/) and specify what exactly your implementation is via vars or tasks (should I support virtualhost? My mysql profile will work as cluster?). In the end, you create the last role (ansible) which will work as a role (puppet pattern). It will add as dependencies your 2 profiles (profile-database, profile-webserver).
In the end, on your playbook, you just apply the role-webserver to the nodes specified.