Ansible is software that automates software provisioning, configuration management, and application deployment. In this post I'll assume that you have some experience with ansible.
Key to writing re-usable roles is to have them do a single thing.
Let's consider this: typical role that installs a database on a server does two things: installs a database and then configures it. This is not too much of a problem until you decide that you want either to install your database differently, and you'd like only to use configuration part. Using only "part" of a role is usually OK, but I try not to have any dead code in my project, and this includes unused ansible tasks.
Recently my provisioning habits are as follows:
Lately I either use dedicated database service (e.g. ovh database) or just provision VM with extra disk and database as docker image.
To configure postgres database server I created a simple i2biz.postgres-init role.
Of course it's hard to decide what is a single thing, for example consider this role: geerlingguy.security (please check out this useful role), it does a couple of distinct things: installs fail2ban, configures ssh somewhat (disables password auth, allows you customize ssh port) and configures automatic updates. You can consider this a single thing: "make my server more secure". However, lately I wanted to override ssh configuration part of geerlingguy.security and I needed to drop it altogether.
I think that Ansible lacks some abstraction levels, basically only easily-shareable abstraction you get in Ansible is a role, which (at least by name) implies that it fully configures a server to perform certain role (e.g. as a web-server).