How to write scalable VHDL code
There is an alternative to passing generics through all levels of the hierarchy:
declare the relevant quantities in a package, and &quot;use&quot; that package in every unit that needs it.
You can do a little better than a constant data_width.
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
 port
    (
        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:
https://stackoverflow.com/questions/35234086/vhdl-standard-layout-syntax-for-header-file/35234901#35234901
https://stackoverflow.com/questions/31383481/vhdl-how-to-define-port-map-of-a-component-with-a-package-in-its-entity/31383565#31383565
https://stackoverflow.com/questions/16144135/avoid-duplicating-code-in-vhdl/16144861#16144861
And libraries
https://stackoverflow.com/questions/13414682/how-to-to-create-include-files-in-vhdl/13415746#13415746
https://stackoverflow.com/questions/44222287/vhdl-library-doesnt-work/44226184#44226184