There is an alternative to passing generics through all levels of the hierarchy:
declare the relevant quantities in a package, and "use" that package in every unit that needs it.
You can do a little better than a constant
package bus_types is
constant DATA_WIDTH: natural := 8;
subtype DATA_BUS is std_logic_vector(DATA_WIDTH - 1 downto 0);
end package bus_types;
Now, any unit that uses the bus_types package
use work.bus_types.all; can simply say
address : in ADDRESS_BUS,
data_in : in DATA_BUS,
data_out : out DATA_BUS
which is simpler and better documents the intent.
There are use cases for generics, and use cases for this. One is not universally better than the other.
Multiple memories, which may be different sizes, are best handled with a generic for memory size. Then the generic specialises the memory block for each usage.
But for a data bus width : the same width is likely to be used throughout the whole design. And then a package like this gets the job done with a lot less plumbing than a generic on every component ... especially if you find yourself passing generics through hierarchy levels, without using them, just to get to a lower level.
More on packages, may supply the additional info requested: